Sherwin Chiu
Published © GPL3+

Tetris Shield on an Arduino Mega

Nothing like a good old game of Tetris on overpowered hardware.

IntermediateShowcase (no instructions)20 hours4,738
Tetris Shield on an Arduino Mega

Things used in this project

Hardware components

Arduino Mega 2560
Arduino Mega 2560
×1
LED Dot Matrix Display, Red
LED Dot Matrix Display, Red
8x8
×2
Shift Register- Serial to Parallel
Texas Instruments Shift Register- Serial to Parallel
×2
Resistor 100 ohm
Resistor 100 ohm
×8
Pushbutton switch 12mm
SparkFun Pushbutton switch 12mm
×5
Resistor 10k ohm
Resistor 10k ohm
×5
IC & Component Socket, 14 Contacts
IC & Component Socket, 14 Contacts
×2
Buzzer, Piezo
Buzzer, Piezo
×1
6-pin Header & Gender Changer (5-pack)
Digilent 6-pin Header & Gender Changer (5-pack)
×1
PCBWay Custom PCB
PCBWay Custom PCB
×1

Software apps and online services

KiCad
KiCad
Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free
Solder Flux, Soldering
Solder Flux, Soldering
Wire Stripper & Cutter, 18-10 AWG / 0.75-4mm² Capacity Wires
Wire Stripper & Cutter, 18-10 AWG / 0.75-4mm² Capacity Wires

Story

Read more

Schematics

Schematic and PCB Contained

Can view the schematic and PCB design

Code

Mega Tetris Code

Arduino
Includes the code to be able to run the Tetris Shield.
#include <avr/wdt.h>
#define   SS1pin   48 // whatever number
#define   SS2pin   46 // whatever number
#define   dataPin     51
#define   clockPin    52

#define   moveLeftPin     35 
#define   moveRightPin    37
#define   rotatePin   33 
#define   fallFasterPin   39
#define   reset  31

#define   OE1              44
#define   OE2              42

#define   BOARD_WIDTH     8
#define   BOARD_HEIGHT    8

byte printBlocks[2][8] = {{
   B00000000, // x = 0
   B00000000, // x = 1
   B00000000, // x = 2
   B00000000, // x = 3 - block[x]; y = 7 + block[y] // bitSet(printBlocks[x], y 
   B00000000,
   B00000000,
   B00000000,
   B00000000,}
  ,{
   B00000000, // x = 0
   B00000000, // x = 1
   B00000000, // x = 2
   B00000000, // x = 3 - block[x]; y = 7 + block[y] // bitSet(printBlocks[x], y 
   B00000000,
   B00000000,
   B00000000,
   B00000000,}};
byte stationaryBlocks[2][8] = {{
   B00000000,
   B00000000, 
   B00000000,
   B00000000,
   B00000000,
   B00000000,
   B00000000,
   B00000000,}
   ,{
   B00000000, // x = 0
   B00000000, // x = 1
   B00000000, // x = 2
   B00000000, // x = 3 - block[x]; y = 7 + block[y] // bitSet(printBlocks[x], y 
   B00000000,
   B00000000,
   B00000000,
   B00000000,}};
byte losingBlocks[8] = {
   B00000000, // x = 0
   B00000000, // x = 1
   B01111100, // x = 2
   B00000100, // x = 3 - block[x]; y = 7 + block[y] // bitSet(printBlocks[x], y 
   B00000100,
   B00000100,
   B00000000,
   B00000000,
  };
unsigned long lastDebounceTime[4] = {0, 0, 0, 0};
unsigned long debounceTime[4] = {0, 0, 0, 0};
boolean currentMatrix = false; //False for top, true for bottom
byte rowNum = 0;
int8_t orientation = 1;
const byte Y = 1; 
const byte X = 0;
// 1, randBlock, 2, which rotation, 3, block placement, 4, coordinates 
int8_t blocks[7][4][4][2] = {   
  {
    //           (default rotation)
    //   o             o o                o
    // o x               x o            x o          o x
    // o                                o              o o
    {{-1, 1}, {-1, 0}, {0, 0}, {0, -1}},
    {{-1, -1}, {0, -1}, {0, 0}, {1, 0}},
    {{1, -1}, {1, 0}, {0, 0}, {0, 1}},
    {{1, 1}, {0, 1}, {0, 0}, {-1, 0}}
  }, {
    //
    // o                 o o           o
    // o x             o x             x o             x o
    //   o                               o           o o
    {{-1, -1}, {-1, 0}, {0, 0}, {0, 1}},
    {{1, -1}, {0, -1}, {0, 0}, {-1, 0}},
    {{1, 1}, {1, 0}, {0, 0}, {0, -1}},
    {{-1, 1}, {0, 1}, {0, 0}, {1, 0}}
  }, {
    //
    //   o             o                o o
    //   x             o x o            x           o x o
    // o o                              o               o
    {{-1, 1}, {0, 1}, {0, 0}, {0, -1}},
    {{-1, -1}, {-1, 0}, {0, 0}, {1, 0}},
    {{1, -1}, {0, -1}, {0, 0}, {0, 1}},
    {{1, 1}, {1, 0}, {0, 0}, {-1, 0}}
  }, {
    //
    // o o                o             o
    //   x            o x o             x           o x o
    //   o                              o o         o
    {{-1, -1}, {0, -1}, {0, 0}, {0, 1}},
    {{1, -1}, {1, 0}, {0, 0}, {-1, 0}},
    {{1, 1}, {0, 1}, {0, 0}, {0, -1}},
    {{-1, 1}, {-1, 0}, {0, 0}, {1, 0}}
  }, {
    //   o                              o
    //   o                              x
    //   x            o x o o           o          o o x o
    //   o                              o
    {{0, -2}, {0, -1}, {0, 0}, {0, 1}},
    {{2, 0}, {1, 0}, {0, 0}, {-1, 0}},
    {{0, 2}, {0, 1}, {0, 0}, {0, -1}},
    {{-2, 0}, {-1, 0}, {0, 0}, {1, 0}}
  }, {
    //
    //   o              o                o
    // o x            o x o              x o         o x o
    //   o                               o             o
    {{0, 1}, {-1, 0}, {0, 0}, {0, -1}},
    {{-1, 0}, {0, -1}, {0, 0}, {1, 0}},
    {{0, -1}, {1, 0}, {0, 0}, {0, 1}},
    {{1, 0}, {0, 1}, {0, 0}, {-1, 0}}
  }, {
    //
    // o o            o o               o o          o o
    // o x            o x               o x          o x
    //
    {{-1, 0}, {-1, -1}, {0, 0}, {0, -1}},
    {{-1, 0}, {-1, -1}, {0, 0}, {0, -1}},
    {{-1, 0}, {-1, -1}, {0, 0}, {0, -1}},
    {{-1, 0}, {-1, -1}, {0, 0}, {0, -1}}
  }
};

int randBlock = 0;

int8_t blockX = 3;
int8_t blockY = 15;

const byte MAX_Y = 15;
const byte MAX_X = 3;
unsigned long timeStart = 0;
unsigned long timeElapsed = 0;

int fallTime = 500;
int losingCounter = 0;

const byte MOVE_RIGHT = 1;
const byte MOVE_LEFT = 2;
const byte ROTATE = 3;
const byte FALL_FASTER = 4;
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  DDRA = B11111111; // all pins are set as outputs
  pinMode(OE1, OUTPUT);
  pinMode(OE2, OUTPUT);
  digitalWrite(OE1, LOW);
  digitalWrite(OE2, LOW);
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(SS1pin, OUTPUT);
  pinMode(SS2pin, OUTPUT);
  pinMode(moveLeftPin, INPUT);
  pinMode(moveRightPin, INPUT);
  pinMode(rotatePin, INPUT);
  pinMode(reset, INPUT);
  pinMode(fallFasterPin, INPUT);
  pinMode(A0, INPUT);
  randomSeed(analogRead(A0));
  createBlock();
  drawBlock(printBlocks);
}
void loop() {
  // put your main code here, to run repeatedly:
  timeStart = millis(); // check current time
  losingScreen();
  outputColumn();   // draw column
  outputRow();      // draw row
  copyStationary();           // copy printBlocks array into stationary blocks
  sidewardCollision();        // Check for sideward collision and correct for it.
  drawMatrixes();
  if((timeStart - timeElapsed) > fallTime){
    blockY--;                           // increment (move block by 1)
    if(downwardCollision()){                      // if collision
      blockY++;                         // move it back
      copyMatrixes();
      createBlock();                    // create a new block
    }
    timeElapsed = timeStart;    // set the timer once more
  }
  moveLeft();   // move left
  moveRight();  // move right
  rotate();     // rotate
  fallFaster(); // fall faster
  resetGame();     // reboot program
}
//---------------------------------------------------------|
//                      Output Functions                   |
//---------------------------------------------------------|
void outputRow(){
  if(currentMatrix)
    shiftData(SS2pin, round(pow(2, rowNum)));
  else if(!currentMatrix)
    shiftData(SS1pin, round(pow(2, rowNum)));
  rowNum++;
  if (rowNum > sizeof(printBlocks[0])){
    currentMatrix=!currentMatrix;
    rowNum = 0;
  }
}
void outputColumn(){
  if(currentMatrix)
    PORTA = ~printBlocks[0][rowNum];
  else if (!currentMatrix)
    PORTA = ~printBlocks[1][rowNum];
}
void shiftData(byte storage, byte data){
  digitalWrite(storage, LOW);
  shiftOut(dataPin, clockPin, LSBFIRST, data);
  digitalWrite(storage, HIGH);
  digitalWrite(storage, LOW);
  shiftOut(dataPin, clockPin, LSBFIRST, 0);
  digitalWrite(storage, HIGH);
}
//---------------------------------------------------------|
//                      Player Functions                   |
//---------------------------------------------------------|
void checkDebounce(byte dir){
  debounceTime[dir] = millis();
  if (debounceTime[dir] - lastDebounceTime[dir] > 200){
    if(dir == MOVE_LEFT)
      blockX++;
    else if(dir == MOVE_RIGHT)
      blockX--;
    else if(dir == ROTATE){
      orientation--;
      if (orientation == -1)
        orientation = 3;
    }
    else if(dir == FALL_FASTER){
      blockY--;
    }
    lastDebounceTime[dir] = debounceTime[dir];
    outputColumn();   // draw column
    outputRow();      // draw row
  }
} // checkDebounce function end 
//--------------------------------------------------------
void moveLeft(){
  if (digitalRead(moveLeftPin))
    checkDebounce(MOVE_LEFT);
}
void moveRight(){
  if (digitalRead(moveRightPin))
    checkDebounce(MOVE_RIGHT);
}
void rotate(){
  if(digitalRead(rotatePin)){
    checkDebounce(ROTATE);
  }
}
void fallFaster(){
  if(digitalRead(fallFasterPin))
    checkDebounce(FALL_FASTER);
}

void resetGame() {
  if(digitalRead(reset)){
    wdt_disable();
    wdt_enable(WDTO_15MS);
    while (1) {}
  }
}
//---------------------------------------------------------|
//                      Block Functions                    |
//---------------------------------------------------------|
void drawMatrixes(){
  drawBlock(printBlocks);     // draw the block to print blocks
  clearBlocks(stationaryBlocks);
}
void copyMatrixes(){
  drawBlock(stationaryBlocks);
//  for(int p = 0; p < 4; p++){
//    if(blockY+ blocks[randBlock][orientation][p][Y] > 7)
//      drawBlock(stationaryBlocks[0]);      // draw current block to the stationaryBlock array
//    else 
//      drawBlock(stationaryBlocks[1]);
//  }
}
void drawBlock(byte copyBlocks[2][8]){
  for(int p = 0; p < 4; p++){
    if(blockY+ blocks[randBlock][orientation][p][Y] > 7)
      bitSet(copyBlocks[0][blockX- blocks[randBlock][orientation][p][X]], blockY-8 + blocks[randBlock][orientation][p][Y]);
    else
      bitSet(copyBlocks[1][blockX- blocks[randBlock][orientation][p][X]], blockY + blocks[randBlock][orientation][p][Y]);
  }
}
void copyStationary(){
  // destination, source, numbytes
  for(int i = 0; i < 2; i++)
    memcpy(printBlocks[i], stationaryBlocks[i], 8);
 
}
void createBlock(){
  randBlock = random(7);
  orientation = 1;
  blockX = MAX_X;
  blockY = MAX_Y;
}
//--------------------------------------------------------
void clearBlocks(byte copyBlocks[2][8]){
  byte checkFullRow = 0;
  for(int q = 0; q < 2; q++){
    for(int i = 0; i < 7; i++){
      for(int j = 0; j < sizeof(copyBlocks[q]); j++){
        checkFullRow += bitRead(copyBlocks[q][j], i); 
      }
      if (checkFullRow == 8){
        checkFullRow = 0;
        for(int n = 0; n < 7; n++){
          bitWrite(copyBlocks[q][n], i, 0);
        }
        for(int j = i+1; j < 7; j++){
          for(int n = 0; n < sizeof(copyBlocks[q]); n++){
            bitWrite(copyBlocks[q][n], j-1, bitRead(copyBlocks[q][n], j));
            bitWrite(copyBlocks[q][n], j, 0);
          }
        }
      }
      checkFullRow = 0;
    } 
    checkFullRow = 0;
  }
}
//---------------------------------------------------------|
//                      Collision Functions                |
//---------------------------------------------------------|
boolean downwardCollision(){
  for(int p = 0; p < 4; p++){
    if(blockY + blocks[randBlock][orientation][p][Y] < 0)
      return true;
    if (blockY+ blocks[randBlock][orientation][p][Y] > 7){
      if(bitRead(stationaryBlocks[0][blockX- blocks[randBlock][orientation][p][X]], blockY-8 + blocks[randBlock][orientation][p][Y]))
        return true;
    } else{
      if(bitRead(stationaryBlocks[1][blockX- blocks[randBlock][orientation][p][X]], blockY + blocks[randBlock][orientation][p][Y]))
        return true;
    }
  }
  return false;
} // downwardCollision function end
//----------------------------------------------------------
void sidewardCollision(){
  for(int p = 0; p < 4; p++){
    // Checking for collision with walls
    if((blockX - blocks[randBlock][orientation][p][X]) >7){
      blockX--;
    }
    else if ((blockX - blocks[randBlock][orientation][p][X]) < 0){
      blockX++;    
    }
    // Checking for collision with other blocks
    if(blockY+ blocks[randBlock][orientation][p][Y] > 7){
      if (bitRead(stationaryBlocks[0][blockX-blocks[randBlock][orientation][p][X]], blockY-8 + blocks[randBlock][orientation][p][Y])){
        blockX--;
      }
      if (bitRead(stationaryBlocks[0][blockX-blocks[randBlock][orientation][p][X]], blockY-8 + blocks[randBlock][orientation][p][Y])){
        blockX+=2;
      }
    } else{
      if (bitRead(stationaryBlocks[1][blockX-blocks[randBlock][orientation][p][X]], blockY + blocks[randBlock][orientation][p][Y])){
        blockX--;
      }
      if (bitRead(stationaryBlocks[1][blockX-blocks[randBlock][orientation][p][X]], blockY + blocks[randBlock][orientation][p][Y])){
        blockX+=2;
      }
    }
  }
} // sidewardsCollision function end
//----------------------------------------------------------
void losingScreen(){
  for(int i = 0; i < 7; i++){
    losingCounter += bitRead(printBlocks[0][i], 7);
  } if (losingCounter > 6){
    memcpy(stationaryBlocks[0], losingBlocks, 8);
    memcpy(stationaryBlocks[1], losingBlocks, 8);
  } else{
    losingCounter = 0;
  }
  
}

Credits

Sherwin Chiu

Sherwin Chiu

7 projects • 8 followers
Just a guy who occasionally blows up capacitors. I love doing this type of stuff!

Comments