About Tetris game speed

Hi everyone.
The TTGOTetris-main got an external button on pin37 which designed for turning the blocks for TTGO.
question:

  1. testing shown the button works even put on other pin like 38?
  2. comment out the turning function, it still works same way, why?
  3. try to use this button to speed up the blocks, not work, why?
 if (digitalRead(SpeedButton) == 0) {
    game_speed = 80;
  } else {
  game_speed = 20;
  }

how to fix please.
Thanks
Adam

#include <SPI.h>
#include <TFT_eSPI.h>
#include "tet.h"


TFT_eSPI tft = TFT_eSPI();

uint16_t BlockImage[8][12][12];                            // Block
uint16_t backBuffer[220][110];                             // GAME AREA
const int Length = 11;     // the number of pixels for a side of a block
const int Width  = 10;     // the number of horizontal blocks
const int Height = 20;     // the number of vertical blocks
int screen[Width][Height] = {0}; //it shows color-numbers of all positions
struct Point {
  int X, Y;
};
struct Block {
  Point square[4][4];
  int numRotate, color;
};
Point pos; Block block;
int rot, fall_cnt = 0;
bool started = false, gameover = false;
boolean but_A = false, but_LEFT = false, but_RIGHT = false;
int game_speed = 20; // 25msec
Block blocks[7] = {
  { { {{ -1, 0}, {0, 0}, {1, 0}, {2, 0}}, {{0, -1}, {0, 0}, {0, 1}, {0, 2}},
      {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}
    }, 2, 1
  },
  { { {{0, -1}, {1, -1}, {0, 0}, {1, 0}}, {{0, 0}, {0, 0}, {0, 0}, {0, 0}},
      {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}
    }, 1, 2
  },
  { { {{ -1, -1}, { -1, 0}, {0, 0}, {1, 0}}, {{ -1, 1}, {0, 1}, {0, 0}, {0, -1}},
      {{ -1, 0}, {0, 0}, {1, 0}, {1, 1}}, {{1, -1}, {0, -1}, {0, 0}, {0, 1}}
    }, 4, 3
  },
  { { {{ -1, 0}, {0, 0}, {0, 1}, {1, 1}}, {{0, -1}, {0, 0}, { -1, 0}, { -1, 1}},
      {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}
    }, 2, 4
  },
  { { {{ -1, 0}, {0, 0}, {1, 0}, {1, -1}}, {{ -1, -1}, {0, -1}, {0, 0}, {0, 1}},
      {{ -1, 1}, { -1, 0}, {0, 0}, {1, 0}}, {{0, -1}, {0, 0}, {0, 1}, {1, 1}}
    }, 4, 5
  },
  { { {{ -1, 1}, {0, 1}, {0, 0}, {1, 0}}, {{0, -1}, {0, 0}, {1, 0}, {1, 1}},
      {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}
    }, 2, 6
  },
  { { {{ -1, 0}, {0, 0}, {1, 0}, {0, -1}}, {{0, -1}, {0, 0}, {0, 1}, { -1, 0}},
      {{ -1, 0}, {0, 0}, {1, 0}, {0, 1}}, {{0, -1}, {0, 0}, {0, 1}, {1, 0}}
    }, 4, 7
  }
};
extern uint8_t tetris_img[];
#define GREY 0x5AEB
int pom = 0;
int pom2 = 0;
int pom3 = 0;
int pom4 = 0;

int score = 0;
int lvl = 1;

int leftButton = 0;
int rightButton = 35;

int SpeedButton = 37;

void setup(void) {

  pinMode(SpeedButton, INPUT_PULLUP); /// originally this button turn the blockes same as the two on board buttons function.
  /// here MDF for spped up
  pinMode(leftButton, INPUT_PULLUP);
  pinMode(rightButton, INPUT_PULLUP);
  pinMode(SpeedButton, INPUT_PULLUP);

  tft.init();
  tft.setRotation(0);
  tft.setSwapBytes(true);

  tft.pushImage(0, 0, 135, 240, tet);
  delay(3000);
  tft.fillScreen(TFT_BLACK);
  tft.drawLine(11, 19, 122, 19, GREY);

  tft.drawLine(11, 19, 11, 240, GREY);
  tft.drawLine(122, 19, 122, 240, GREY);

  tft.drawString("SCORE:" + String(score), 14, 8, 1);
  tft.drawString("LVL:" + String(lvl), 88, 8, 1);
  //----------------------------// Make Block ----------------------------
  make_block( 0, TFT_BLACK);        // Type No, Color
  make_block( 1, 0x00F0);       // DDDD     RED
  make_block( 2, 0xFBE4);       // DD,DD    PUPLE
  make_block( 3, 0xFF00);       // D__,DDD  BLUE
  make_block( 4, 0xFF87);       // DD_,_DD  GREEN
  make_block( 5, 0x87FF);       // __D,DDD  YELLO
  make_block( 6, 0xF00F);       // _DD,DD_  LIGHT GREEN
  make_block( 7, 0xF8FC);       // _D_,DDD  PINK
  //----------------------------------------------------------------------


  PutStartPos();                             // Start Position
  for (int i = 0; i < 4; ++i) screen[pos.X +
                                       block.square[rot][i].X][pos.Y + block.square[rot][i].Y] = block.color;
  Draw();                                    // Draw block
}
//========================================================================
void loop() {
  if (gameover) {
    if (digitalRead(leftButton) == 0)
    {
      for (int j = 0; j < Height; ++j)
        for (int i = 0; i < Width; ++i)
          screen[i][j] = 0;
      gameover = false;
      score = 0;
      game_speed = 20;
      lvl = 1;
      PutStartPos();                             // Start Position
      for (int i = 0; i < 4; ++i) screen[pos.X +
                                           block.square[rot][i].X][pos.Y + block.square[rot][i].Y] = block.color;
      tft.drawString("SCORE:" + String(score), 14, 8, 1);
      tft.drawString("LVL:" + String(lvl), 88, 8, 1);
      Draw();
    }
    return;
  }

  if (gameover == false) {
    Point next_pos;
    int next_rot = rot;
    GetNextPosRot(&next_pos, &next_rot);
    ReviseScreen(next_pos, next_rot);
    //M5.update();
    delay(game_speed);
  }                                  // SPEED ADJUST
}
//========================================================================
void Draw() {                               // Draw 120x240 in the center
  for (int i = 0; i < Width; ++i) for (int j = 0; j < Height; ++j)
      for (int k = 0; k < Length; ++k) for (int l = 0; l < Length; ++l)
          backBuffer[j * Length + l][i * Length + k] = BlockImage[screen[i][j]][k][l];
  tft.pushImage(12, 20, 110, 220, *backBuffer);
}
//========================================================================
void PutStartPos() {
  game_speed = 20;
  pos.X = 4; pos.Y = 1;
  block = blocks[random(7)];
  rot = random(block.numRotate);
}
//========================================================================
bool GetSquares(Block block, Point pos, int rot, Point* squares) {
  bool overlap = false;
  for (int i = 0; i < 4; ++i) {
    Point p;
    p.X = pos.X + block.square[rot][i].X;
    p.Y = pos.Y + block.square[rot][i].Y;
    overlap |= p.X < 0 || p.X >= Width || p.Y < 0 || p.Y >=
               Height || screen[p.X][p.Y] != 0;
    squares[i] = p;
  }
  return !overlap;
}
//========================================================================
void GameOver() {
  for (int i = 0; i < Width; ++i) for (int j = 0; j < Height; ++j)
      if (screen[i][j] != 0) screen[i][j] = 4;
  gameover = true;

}
//========================================================================
void ClearKeys() {
  but_A = false;
  but_LEFT = false;
  but_RIGHT = false;
}
//========================================================================

bool KeyPadLoop() {
  if (digitalRead(leftButton) == 0 && digitalRead(rightButton) == 1) {
    if (pom == 0)
    {
      pom = 1;
      ClearKeys();
      but_LEFT = true;
      return true;
    }
  } else {
    pom = 0;
  }

  if (digitalRead(rightButton) == 0 && digitalRead(leftButton) == 1) {
    if (pom2 == 0)
    {
      pom2 = 1;
      ClearKeys();
      but_RIGHT = true;
      return true;
    }
  } else {
    pom2 = 0;
  }

  /*
    if(digitalRead(37)==0){
     if(pom3==0)
    {pom3=1;ClearKeys();but_A    =true;return true;}
    }else {pom3=0;}
  */

  if (digitalRead(SpeedButton) == 0) {
    game_speed = 80;
  } else {
  game_speed = 20;
  }

  if (digitalRead(rightButton) == 0 && digitalRead(leftButton) == 0) {
    if (pom4 == 0)
    {
      pom4 = 1;
      ClearKeys();
      but_A = true;
      return true;
    }
  } else {
    pom4 = 0;
  }
  return false;
}
//========================================================================
void GetNextPosRot(Point* pnext_pos, int* pnext_rot) {
  bool received = KeyPadLoop();

  if (but_LEFT) started = true;
  if (!started) return;
  pnext_pos->X = pos.X;
  pnext_pos->Y = pos.Y;
  if ((fall_cnt = (fall_cnt + 1) % 10) == 0) pnext_pos->Y += 1;
  else if (1) {
    if (but_LEFT) {
      but_LEFT = false;
      pnext_pos->X -= 1;
    }
    else if (but_RIGHT) {
      but_RIGHT = false;
      pnext_pos->X += 1;
    }
    else if (but_A) {
      but_A = false;
      *pnext_rot = (*pnext_rot + block.numRotate - 1) % block.numRotate;
    }
  }
}
//========================================================================
void DeleteLine() {
  for (int j = 0; j < Height; ++j) {
    bool Delete = true;
    for (int i = 0; i < Width; ++i) if (screen[i][j] == 0) Delete = false;
    if (Delete)
    {
      score++;
      if (score % 5 == 0)
      {
        lvl++;
        game_speed = game_speed - 4;
        tft.drawString("LVL:" + String(lvl), 88, 8, 1);
      }
      tft.drawString("SCORE:" + String(score), 14, 8, 1);
      for (int k = j; k >= 1; --k)
      {


        for (int i = 0; i < Width; ++i)
        {
          screen[i][k] = screen[i][k - 1];
        }
      }
    }
  }
}
//========================================================================
void ReviseScreen(Point next_pos, int next_rot) {
  if (!started) return;
  Point next_squares[4];
  for (int i = 0; i < 4; ++i) screen[pos.X +
                                       block.square[rot][i].X][pos.Y + block.square[rot][i].Y] = 0;
  if (GetSquares(block, next_pos, next_rot, next_squares)) {
    for (int i = 0; i < 4; ++i) {
      screen[next_squares[i].X][next_squares[i].Y] = block.color;
    }
    pos = next_pos; rot = next_rot;
  }
  else {
    for (int i = 0; i < 4; ++i) screen[pos.X +
                                         block.square[rot][i].X][pos.Y + block.square[rot][i].Y] = block.color;
    if (next_pos.Y == pos.Y + 1) {
      DeleteLine(); PutStartPos();
      if (!GetSquares(block, pos, rot, next_squares)) {
        for (int i = 0; i < 4; ++i) screen[pos.X +
                                             block.square[rot][i].X][pos.Y + block.square[rot][i].Y] = block.color;
        GameOver();
      }
    }
  }
  Draw();
}
//========================================================================
void make_block( int n , uint16_t color ) {           // Make Block color
  for ( int i = 0 ; i < 12; i++ ) for ( int j = 0 ; j < 12; j++ ) {
      BlockImage[n][i][j] = color;                           // Block color
      if ( i == 0 || j == 0 ) BlockImage[n][i][j] = 0;       // TFT_BLACK Line
    }
}
//========================================================================

That's the only use of game_speed. Put your button logic hack of the value right there.

That will keep all the other changes that are made to that variable irrelevant.

a7

Great!
Thank you.
let me ask one more question here please.
I modified the setup() as Tetris_setup();, loop() as Tetris_loop();
and used a new file filled as:

#include "CONFIG.h"

void setup() {
  // put your setup code here, to run once:
  Serial.print(115200);
  Serial.print("setup!!!!!!!!!!!!!!!!!");
  pinMode(buttonA, INPUT_PULLUP);
  digitalWrite(buttonA, HIGH);
}

void loop() {
  // put your main code here, to run repeatedly:
  if (digitalRead (buttonA) == 0)
  {
    TetrisSETBJ = 1;
    Serial.print("buttonA=");
    Serial.println(digitalRead(buttonA));
  }

// if (TetrisSETBJ == 1)
//  {
    Tetris_setup();
//  }
}

the game started when pressed buttonA.
but if I used:

if (TetrisSETBJ == 1)
  {
    Tetris_setup();
 }

the game doesn't run, why?
Thanks for help please.

CONFIG.zip (52.8 KB)


You meant

    Serial.begin(115200);

Dunno what you expect from your code. I read it, and it does what you programmed it to.


Play with it here in the wokwi simulator.

I made a few tweaks, but nothing substantive. All I noticed was it picked up a bit of switch bounce, mostly because you needlessly digitlRead() the button when you already know its state - by the time you look again, it might have bounced and given the opposite reading.

//#include "CONFIG.h"
// https://forum.arduino.cc/t/about-tetris-game-speed/1062433

# define buttonA  7
unsigned char TetrisSETBJ;

void setup() {
  Serial.begin(115200);
  Serial.println("setup.");
  pinMode(buttonA, INPUT_PULLUP);
  digitalWrite(buttonA, HIGH);
}

void loop() {

  if (digitalRead (buttonA) == 0)
  {
    TetrisSETBJ = 1;
    Serial.print("buttonA = ");
    Serial.println(digitalRead(buttonA));
  }

  if (TetrisSETBJ == 1) {
    Tetris_setup();
  }

  TetrisSETBJ = 0;
}

void Tetris_setup()
{
  static int counta;

  Serial.print("Tetris setup.     ");
  Serial.println(counta);

  counta++;
}

HTH

a7

Thank you, sorry my typo.

the code is play Tetris game, you may check the #1 post.
what I did is use another file to call the Tetris_setup() and which call Tetris_loop() by button pressed.
the problem here is when calling Tetris_setup() directly, the game works well.
if I call it by if() command, it doesn't work, why?

// if (TetrisSETBJ == 1)  // call with if condition 
//  {
    Tetris_setup();  // directly call 
//  }

the modified sketch attached in #3

Sry, no clue. As you can see by the little demo I made, there is nothing wrong with using (or not using) a test to deicde whether to call a function.

Without setting up your entire sketch, it would be impossible to begin to try to understand.

Setting up your entire sketch requires hardware I do not have, not to mention time... I don't know if the wokwi would support all your hardware. If you get Tetris running in the simulator, still misbehaving, it would be easier to take a look.

But I suggest that you use this as an opportunity to read deeper into the code you did not write, and to improve you diagnostic skills and techniques, starting with serial printing to verify the values of key variables at places where they are significant and to demonstrate that your program is flowing like you think it should.

And to that I add good luck. More code, more problems. Code you didn't write... can be the source of all kindsa grief if you don't use it just so. I reiterate the importance of understanding the code you didn't write.

a7

Thank you alto777 for your time and good advice.
I'll check into more deep for it.
Have a great day.