Tetris on Addressible LED Display

So im a high school student, making tetris on an addressable LED display with a mega. The problem is with this collision function I wrote to check if the active piece is colliding with the bottom of the display or the "well" which is the pieces that have already landed. The function checks if the coordinates of the piece are equal to coordinates in the well and if they are, it returns true. I assume the for-each loop Im using should check all of the coordinates for the piece but some of them are not working while others do. It feels like I'm somehow checking the collision wrong but this is starting to get out of my reach

not sure where to go from here any guidance would be greatly appreciated

I'll try to attach a video so you can see what the problem is

Here's my code, sorry it's so ugly I'm a noob

#include <FastLED.h>
#include <LEDMatrix.h>

#define DATA_PIN            5

#define COLOR_ORDER         GRB
#define CHIPSET             WS2812B

#define MATRIX_WIDTH        8
#define MATRIX_HEIGHT       32
#define MATRIX_TYPE         HORIZONTAL_ZIGZAG_MATRIX
#define MATRIX_SIZE         (MATRIX_WIDTH*MATRIX_HEIGHT)
#define NUMPIXELS           MATRIX_SIZE

cLEDMatrix<MATRIX_WIDTH, MATRIX_HEIGHT, MATRIX_TYPE> leds;

class Tetramino {
 public:
   int pieceNum;

   int r;
   int g;
   int b;

   int xpos [4];
   int ypos [4];
   int nx = 2;
   int ny = 28;

   int fallSpeed = 100;

   int well[32][8];

   //matrices defining each block. 3d array, 7 element list of 4x4 matrices
   int pieces [7][4][4] = {
     { //pieceNum 0, O-Block
       {0, 0, 0, 0},
       {0, 0, 0, 0},
       {1, 1, 0, 0},
       {1, 1, 0, 0}
     },
     { //pieceNum 1, I-Block
       {1, 0, 0, 0},
       {1, 0, 0, 0},
       {1, 0, 0, 0},
       {1, 0, 0, 0}
     },
     { //pieceNum 2, J-Block
       {0, 0, 0, 0},
       {0, 1, 0, 0},
       {0, 1, 0, 0},
       {1, 1, 0, 0}
     },
     { //pieceNum 3, L-Block
       {0, 0, 0, 0},
       {1, 0, 0, 0},
       {1, 0, 0, 0},
       {1, 1, 0, 0}
     },
     { //pieceNum 4, T-Block
       {0, 0, 0, 0},
       {0, 0, 0, 0},
       {0, 1, 0, 0},
       {1, 1, 1, 0}
     },
     { //pieceNum 5, S-Block
       {0, 0, 0, 0},
       {0, 0, 0, 0},
       {0, 1, 1, 0},
       {1, 1, 0, 0}
     },
     { //pieceNum 6, Z-Block
       {0, 0, 0, 0},
       {0, 0, 0, 0},
       {1, 1, 0, 0},
       {0, 1, 1, 0}
     }
   };

   void getPieceNum() {
     pieceNum = random(0, 7);
   }

   void getPiece(int pieceNum) {

     //colors for each block
     if (pieceNum == 0) {
       r = 255; g = 200; b = 0;
     }
     if (pieceNum == 1) {
       r = 0; g = 255; b = 200;
     }
     if (pieceNum == 2) {
       r = 0; g = 0; b = 255;
     }
     if (pieceNum == 3) {
       r = 255; g = 75; b = 0;
     }
     if (pieceNum == 4) {
       r = 150; g = 0; b = 255;
     }
     if (pieceNum == 5) {
       r = 0; g = 255; b = 0;
     }
     if (pieceNum == 6) {
       r = 255; g = 0; b = 0;
     }

     //read maxtrix of pieceNum and draw a pixel where there is a 1
     int count = 0;
     for (int row = 0; row < 4; row++) {
       for (int col = 0; col < 4; col ++) {
         if (pieces[pieceNum][row][col] == 1) {
           if (count < 4) {
             xpos[count] = col;
             ypos[count] = row;
             count++;
           }
         }
       }
     }

     for (int i = 0; i < 4; i++) {
       leds.DrawPixel(xpos[i] + nx, ypos[i] + ny, CRGB(r, g, b));
     }
   }

   void shiftRight() {
     if (nx < 6) {
       if (analogRead(A1) > 1000) {
         nx += 1;
       }
     }
   }

   void shiftLeft() {
     if (nx > 0) {
       if (analogRead(A1) < 100) {
         nx -= 1;
       }
     }
   }

   void softDrop() {
     if (analogRead(A2) < 100) {
       fallSpeed = 25;
     }
     else {
       fallSpeed = 100;
     }
   }

   void hardDrop() {
     if (analogRead(A2) > 1000) {
       ny = 0;
     }
   }

   void fall() {
     getPiece(pieceNum);
     FastLED.show();
     delay(fallSpeed);
     FastLED.clear();
     rotate();
     shiftLeft();
     shiftRight();
     softDrop();
     //hardDrop();
     ny--;
   }

   void addToWell() {
     for (int i = 0; i < 4; i++) {
       if (well[ypos[i] + ny][xpos[i] + nx] == 0) {
         well[ypos[i] + ny][xpos[i] + nx] = 1;
       }
     }
   }

   void showWell() {
     for (int row = 0; row < 32; row++) {
       for (int col = 0; col < 8; col++) {
         if (well[row][col] == 1) {
           leds.DrawPixel(col, row, CRGB(255, 0, 0));
         }
       }
     }
   }

   boolean collide() {
     for (int i : ypos) {
       for (int j : xpos) {
         if (well[i + ny - 1][j + nx] == 1 || i + ny == 0) {
           return true;
         }
         else {
           return false;
         }
       }
     }
   };

   void rotate() {
     if (digitalRead(4) == 1) {
       for (int i = 0; i < 2; i++) {
         for (int j = 0; j < 3 - i; j++) {
           int temp = pieces[pieceNum][i][j];
           pieces[pieceNum][i][j] = pieces[pieceNum][3 - j][i];
           pieces[pieceNum][3 - j][i] = pieces[pieceNum][3 - i][3 - j];
           pieces[pieceNum][3 - i][3 - j] = pieces[pieceNum][j][3 - i];
           pieces[pieceNum][j][3 - i] = temp;
         }
       }
     }
   }
};

Tetramino tetramino = Tetramino();

void setup() {
 FastLED.addLeds<CHIPSET, DATA_PIN, COLOR_ORDER>(leds[0], leds.Size()).setCorrection(TypicalSMD5050);
 FastLED.setCorrection(TypicalLEDStrip);
 FastLED.setBrightness(50);

 randomSeed(analogRead(A0));

 Serial.begin(9600);

 tetramino.getPieceNum();
 tetramino.getPiece(tetramino.pieceNum);
 FastLED.clear();
}

void loop() {
 if (tetramino.collide() != true) {
   tetramino.fall();
   tetramino.showWell();
 }
 if (tetramino.collide() == true) {
   tetramino.addToWell();
   tetramino.showWell();
   FastLED.show();
   tetramino.getPieceNum();
   tetramino.ny = 28;
 }
}

Hi DomGallo --

Looking at your code I see a few things that are likely causing some issues for you:

  • When you call DrawPixel(), I think you were intending to loop through all 4 coordinates of the block, but instead you are passing in (xpos+nx,ypos+ny). xpos and ypos are arrays containing the coordinates, so you will want to use your "i" index into these coordinate arrays. For example: leds.DrawPixel(nx+xpos[i ],ny+ypos[i ], CRGB(r,g,b));
  • In addToWell(), you have a similar issue, so you should replace with well[ny+ypos[i ]][nx+xpos[i ]] = 1;
  • The rotate() function is missing an array index. You have a 3-dimensional array but in most cases you are only assigning with two subscripts. For example, the following code should be able to perform your in-place matrix rotate counter-clockwise:
    for (int nX = 0; nX < 2; nX++) 
    { 
        for (int nY = nX; nY < 3-nX; nY++) 
        { 
            int nTemp = pieces[pieceNum][nX][nY]; 
            pieces[pieceNum][nX][y] = pieces[pieceNum][nY][3-nX]; 
            pieces[pieceNum][nY][3-nX] = pieces[pieceNum][3-nX][3-nY]; 
            pieces[pieceNum][3-nX][3-nY] = pieces[pieceNum][3-nY][nX]; 
            pieces[pieceNum][3-nY][nX] = nTemp; 
        } 
    }

I recommend you try to clean these parts up before moving on to the collision detection logic (the collide() for-loop statements do appear problematic).

Hope that helps!

EDIT: sorry about the repost, i couldn't find the last one so I thought maybe it didn't send. thanks for the help

I'm a high school student making tetris for an addressable LED matrix. Mostly been going pretty well but i have hit a roadblock with this collision function I've been writing. The idea is that the falling pieces will 'collide' when they hit any coordinate that has been added to the well array which is all the pieces that have landed already. This works sometimes, but occasionally the pieces will continue through the well blocks. In other words, only some of the values are working for the for-each loops that I have checking the position of the pieces.

I'm not really sure where to go from here, any help would be really appreciated

here's my code, sorry it's so gross:

#include <FastLED.h>
#include <LEDMatrix.h>

#define DATA_PIN            5

#define COLOR_ORDER         GRB
#define CHIPSET             WS2812B

#define MATRIX_WIDTH        8
#define MATRIX_HEIGHT       32
#define MATRIX_TYPE         HORIZONTAL_ZIGZAG_MATRIX
#define MATRIX_SIZE         (MATRIX_WIDTH*MATRIX_HEIGHT)
#define NUMPIXELS           MATRIX_SIZE

cLEDMatrix<MATRIX_WIDTH, MATRIX_HEIGHT, MATRIX_TYPE> leds;

class Tetramino {
 public:
   int pieceNum;

   int r;
   int g;
   int b;

   int xpos [4];
   int ypos [4];
   int nx = 2;
   int ny = 28;

   int fallSpeed = 100;

   int well[32][8];

   //matrices defining each block. 3d array, 7 element list of 4x4 matrices
   int pieces [7][4][4] = {
     { //pieceNum 0, O-Block
       {0, 0, 0, 0},
       {0, 0, 0, 0},
       {1, 1, 0, 0},
       {1, 1, 0, 0}
     },
     { //pieceNum 1, I-Block
       {1, 0, 0, 0},
       {1, 0, 0, 0},
       {1, 0, 0, 0},
       {1, 0, 0, 0}
     },
     { //pieceNum 2, J-Block
       {0, 0, 0, 0},
       {0, 1, 0, 0},
       {0, 1, 0, 0},
       {1, 1, 0, 0}
     },
     { //pieceNum 3, L-Block
       {0, 0, 0, 0},
       {1, 0, 0, 0},
       {1, 0, 0, 0},
       {1, 1, 0, 0}
     },
     { //pieceNum 4, T-Block
       {0, 0, 0, 0},
       {0, 0, 0, 0},
       {0, 1, 0, 0},
       {1, 1, 1, 0}
     },
     { //pieceNum 5, S-Block
       {0, 0, 0, 0},
       {0, 0, 0, 0},
       {0, 1, 1, 0},
       {1, 1, 0, 0}
     },
     { //pieceNum 6, Z-Block
       {0, 0, 0, 0},
       {0, 0, 0, 0},
       {1, 1, 0, 0},
       {0, 1, 1, 0}
     }
   };

   void getPieceNum() {
     pieceNum = random(0, 7);
   }

   void getPiece(int pieceNum) {

     //colors for each block
     if (pieceNum == 0) {
       r = 255; g = 200; b = 0;
     }
     if (pieceNum == 1) {
       r = 0; g = 255; b = 200;
     }
     if (pieceNum == 2) {
       r = 0; g = 0; b = 255;
     }
     if (pieceNum == 3) {
       r = 255; g = 75; b = 0;
     }
     if (pieceNum == 4) {
       r = 150; g = 0; b = 255;
     }
     if (pieceNum == 5) {
       r = 0; g = 255; b = 0;
     }
     if (pieceNum == 6) {
       r = 255; g = 0; b = 0;
     }

     //read maxtrix of pieceNum and draw a pixel where there is a 1
     int count = 0;
     for (int row = 0; row < 4; row++) {
       for (int col = 0; col < 4; col ++) {
         if (pieces[pieceNum][row][col] == 1) {
           if (count < 4) {
             xpos[count] = col;
             ypos[count] = row;
             count++;
           }
         }
       }
     }

     for (int i = 0; i < 4; i++) {
       leds.DrawPixel(xpos[i] + nx, ypos[i] + ny, CRGB(r, g, b));
     }
   }

   void shiftRight() {
     if (nx < 6) {
       if (analogRead(A1) > 1000) {
         nx += 1;
       }
     }
   }

   void shiftLeft() {
     if (nx > 0) {
       if (analogRead(A1) < 100) {
         nx -= 1;
       }
     }
   }

   void softDrop() {
     if (analogRead(A2) < 100) {
       fallSpeed = 25;
     }
     else {
       fallSpeed = 100;
     }
   }

   void hardDrop() {
     if (analogRead(A2) > 1000) {
       ny = 0;
     }
   }

   void fall() {
     getPiece(pieceNum);
     FastLED.show();
     delay(fallSpeed);
     FastLED.clear();
     shiftLeft();
     shiftRight();
     softDrop();
     //hardDrop();
     ny--;
   }

   void addToWell() {
     for (int i = 0; i < 4; i++) {
       if (well[ypos[i] + ny][xpos[i] + nx] == 0) {
         well[ypos[i] + ny][xpos[i] + nx] = 1;
       }
     }
   }

   void showWell() {
     for (int row = 0; row < 32; row++) {
       for (int col = 0; col < 8; col++) {
         if (well[row][col] == 1) {
           leds.DrawPixel(col, row, CRGB(255, 0, 0));
         }
       }
     }
   }

   boolean collide() {
     for (int i : ypos) {
       for (int j : xpos) {
         if (well[i + ny - 1][j + nx] == 1 || i + ny == 0) {
           return true;
         }
         else {
           return false;
         }
       }
     }
   };

};

Tetramino tetramino = Tetramino();

void setup() {
 FastLED.addLeds<CHIPSET, DATA_PIN, COLOR_ORDER>(leds[0], leds.Size()).setCorrection(TypicalSMD5050);
 FastLED.setCorrection(TypicalLEDStrip);
 FastLED.setBrightness(50);

 randomSeed(analogRead(A0));

 Serial.begin(9600);

 tetramino.getPieceNum();
 tetramino.getPiece(tetramino.pieceNum);
 FastLED.clear();
}

void loop() {
 if (tetramino.collide() != true) {
   tetramino.fall();
   tetramino.showWell();
 }
 if (tetramino.collide() == true) {
   tetramino.addToWell();
   tetramino.showWell();
   FastLED.show();
   tetramino.getPieceNum();
   tetramino.ny = 28;
 }
}

@ impulsive
thanks for the insight, again sorry my code's so gross

Hi DomGallo: for reference, I had responded to your original post here: Tetris on Adressable LED display

@impulsive
I saw that and thank you, I appreciate the feedback. I wasnt sure if the original posted so I thought I would put it up again

Impulsive:
Hi DomGallo --

Looking at your code I see a few things that are likely causing some issues for you:

  • When you call DrawPixel(), I think you were intending to loop through all 4 coordinates of the block, but instead you are passing in (xpos+nx,ypos+ny). xpos and ypos are arrays containing the coordinates, so you will want to use your "i" index into these coordinate arrays. For example: leds.DrawPixel(nx+xpos[i ],ny+ypos[i ], CRGB(r,g,b));
  • In addToWell(), you have a similar issue, so you should replace with well[ny+ypos[i ]][nx+xpos[i ]] = 1;

Again, sorry, I'm new to all this, I guess the code tags are important. For whatever reason - maybe because i neglected to use code tags - it got changed when I posted it so the indexes that I had weren't there. not sure how that happened but this is what it should actually look like:

#include <FastLED.h>
#include <LEDMatrix.h>

#define DATA_PIN            5

#define COLOR_ORDER         GRB
#define CHIPSET             WS2812B

#define MATRIX_WIDTH        8
#define MATRIX_HEIGHT       32
#define MATRIX_TYPE         HORIZONTAL_ZIGZAG_MATRIX
#define MATRIX_SIZE         (MATRIX_WIDTH*MATRIX_HEIGHT)
#define NUMPIXELS           MATRIX_SIZE

cLEDMatrix<MATRIX_WIDTH, MATRIX_HEIGHT, MATRIX_TYPE> leds;

class Tetramino {
  public:
    int pieceNum;

    int r;
    int g;
    int b;

    int xpos [4];
    int ypos [4];
    int nx = 2;
    int ny = 28;

    int fallSpeed = 100;

    int well[32][8];

    //matrices defining each block. 3d array, 7 element list of 4x4 matrices
    int pieces [7][4][4] = {
      { //pieceNum 0, O-Block
        {0, 0, 0, 0},
        {0, 0, 0, 0},
        {1, 1, 0, 0},
        {1, 1, 0, 0}
      },
      { //pieceNum 1, I-Block
        {1, 0, 0, 0},
        {1, 0, 0, 0},
        {1, 0, 0, 0},
        {1, 0, 0, 0}
      },
      { //pieceNum 2, J-Block
        {0, 0, 0, 0},
        {0, 1, 0, 0},
        {0, 1, 0, 0},
        {1, 1, 0, 0}
      },
      { //pieceNum 3, L-Block
        {0, 0, 0, 0},
        {1, 0, 0, 0},
        {1, 0, 0, 0},
        {1, 1, 0, 0}
      },
      { //pieceNum 4, T-Block
        {0, 0, 0, 0},
        {0, 0, 0, 0},
        {0, 1, 0, 0},
        {1, 1, 1, 0}
      },
      { //pieceNum 5, S-Block
        {0, 0, 0, 0},
        {0, 0, 0, 0},
        {0, 1, 1, 0},
        {1, 1, 0, 0}
      },
      { //pieceNum 6, Z-Block
        {0, 0, 0, 0},
        {0, 0, 0, 0},
        {1, 1, 0, 0},
        {0, 1, 1, 0}
      }
    };

    void getPieceNum() {
      pieceNum = random(0, 7);
    }

    void getPiece(int pieceNum) {

      //colors for each block
      if (pieceNum == 0) {
        r = 255; g = 200; b = 0;
      }
      if (pieceNum == 1) {
        r = 0; g = 255; b = 200;
      }
      if (pieceNum == 2) {
        r = 0; g = 0; b = 255;
      }
      if (pieceNum == 3) {
        r = 255; g = 75; b = 0;
      }
      if (pieceNum == 4) {
        r = 150; g = 0; b = 255;
      }
      if (pieceNum == 5) {
        r = 0; g = 255; b = 0;
      }
      if (pieceNum == 6) {
        r = 255; g = 0; b = 0;
      }

      //read maxtrix of pieceNum and draw a pixel where there is a 1
      int count = 0;
      for (int row = 0; row < 4; row++) {
        for (int col = 0; col < 4; col ++) {
          if (pieces[pieceNum][row][col] == 1) {
            if (count < 4) {
              xpos[count] = col;
              ypos[count] = row;
              count++;
            }
          }
        }
      }

      for (int i = 0; i < 4; i++) {
        leds.DrawPixel(xpos[i] + nx, ypos[i] + ny, CRGB(r, g, b));
      }
    }

    void shiftRight() {
      if (nx < 6) {
        if (analogRead(A1) > 1000) {
          nx += 1;
        }
      }
    }

    void shiftLeft() {
      if (nx > 0) {
        if (analogRead(A1) < 100) {
          nx -= 1;
        }
      }
    }

    void softDrop() {
      if (analogRead(A2) < 100) {
        fallSpeed = 25;
      }
      else {
        fallSpeed = 100;
      }
    }

    void hardDrop() {
      if (analogRead(A2) > 1000) {
        ny = 0;
      }
    }

    void fall() {
      getPiece(pieceNum);
      FastLED.show();
      delay(fallSpeed);
      FastLED.clear();
      rotate();
      shiftLeft();
      shiftRight();
      softDrop();
      //hardDrop();
      ny--;
    }

    void addToWell() {
      for (int i = 0; i < 4; i++) {
        if (well[ypos[i] + ny][xpos[i] + nx] == 0) {
          well[ypos[i] + ny][xpos[i] + nx] = 1;
        }
      }
    }

    void showWell() {
      for (int row = 0; row < 32; row++) {
        for (int col = 0; col < 8; col++) {
          if (well[row][col] == 1) {
            leds.DrawPixel(col, row, CRGB(255, 0, 0));
          }
        }
      }
    }

    boolean collide() {
      for (int i : ypos) {
        for (int j : xpos) {
          if (well[i + ny - 1][j + nx] == 1 || i + ny == 0) {
            return true;
          }
          else {
            return false;
          }
        }
      }
    };

    void rotate() {
      if (digitalRead(4) == 1) {
        for (int nX = 0; nX < 2; nX++) {
          for (int nY = nX; nY < 3 - nX; nY++) {
            int nTemp = pieces[pieceNum][nX][nY];
            pieces[pieceNum][nX][nY] = pieces[pieceNum][nY][3 - nX];
            pieces[pieceNum][nY][3 - nX] = pieces[pieceNum][3 - nX][3 - nY];
            pieces[pieceNum][3 - nX][3 - nY] = pieces[pieceNum][3 - nY][nX];
            pieces[pieceNum][3 - nY][nX] = nTemp;
          }
        }
      }
    }
};

Tetramino tetramino = Tetramino();

void setup() {
  FastLED.addLeds<CHIPSET, DATA_PIN, COLOR_ORDER>(leds[0], leds.Size()).setCorrection(TypicalSMD5050);
  FastLED.setCorrection(TypicalLEDStrip);
  FastLED.setBrightness(50);

  randomSeed(analogRead(A0));

  Serial.begin(9600);

  tetramino.getPieceNum();
  tetramino.getPiece(tetramino.pieceNum);
  FastLED.clear();
}

void loop() {
  if (tetramino.collide() != true) {
    tetramino.fall();
    tetramino.showWell();
  }
  if (tetramino.collide() == true) {
    tetramino.addToWell();
    tetramino.showWell();
    FastLED.show();
    tetramino.getPieceNum();
    tetramino.ny = 28;
  }
}

Without code tags, your [ i ] array subscripts were treated by the forum software as an indicator to change to italics mode, so it conveniently dropped them for you :slight_smile: thanks for posting the code-tagged source.

For your collide() function, I think the intention of your code was to loop through all of the current piece's coordinates and find a match against the blocks already in the well.

As written, the nested for loop does not look right. Because of the way you have already extracted coordinates from your pieces into the xpos/ypos arrays, you should not need to use a nested for loop.

Also note that your "return false" is probably preventing correct collision detection as it is breaking out of the loop on the first non-hit check.

My recommendation would be for the following:

  • Use a single for-loop across the coordinates in your current piece (ie. for (int nBlockInd=0;nBlockInd<4;nBlockInd++)
  • Then extract out the X & Y coordinates from the xpos[] and ypos[]
  • Now compare the state of the well[][] for this particular coordinate (one row down) and also check for the floor row
  • Return true on the first collision hit and only return false outside of your loop (this way it only returns false if all 4 iterations of your loop don't find a collision)

ohhhhhhhh, I hadn't thought of the return false as restricting the loop, very helpful, thank you

For people who are wondering; the includes at the top of the script are:

#include "FastLED.h"
#include "LEDMatrix.h"

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.