Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Arduino ile tetris oyunu uygulaması
/*
* Bu tetris uygulaması https://github.com/AJRussell/Tiny-Tetris 'dan Faydalanılarak geliştirilmiştir.
* https://create.arduino.cc/projecthub/BADFEED/tetris-clone-with-oled-ssd1306-i2c-for-arduino-nano-uno-8d9395 ilgili linkten orjinal sürümüne gidebilirsiniz.
akademi.robolinkmarket.com
*/
#include <Wire.h>
#include "TetrisTheme.cpp"
#include "dpad.cpp"
#define OLED_ADDRESS 0x3C //you may need to change this, this is the OLED I2C address.
#define OLED_COMMAND 0x80
#define OLED_DATA 0x40
#define OLED_DISPLAY_OFF 0xAE
#define OLED_DISPLAY_ON 0xAF
#define OLED_NORMAL_DISPLAY 0xA6
#define OLED_INVERSE_DISPLAY 0xA7
#define OLED_SET_BRIGHTNESS 0x81
#define OLED_SET_ADDRESSING 0x20
#define OLED_HORIZONTAL_ADDRESSING 0x00
#define OLED_VERTICAL_ADDRESSING 0x01
#define OLED_PAGE_ADDRESSING 0x02
#define OLED_SET_COLUMN 0x21
#define OLED_SET_PAGE 0x22
// the tetris blocks
const byte Blocks[7][2] PROGMEM = {
{ 0B01000100, 0B01000100 },
{ 0B11000000, 0B01000100 },
{ 0B01100000, 0B01000100 },
{ 0B01100000, 0B00000110 },
{ 0B11000000, 0B00000110 },
{ 0B01000000, 0B00001110 },
{ 0B01100000, 0B00001100 }
};
// the numbers for score, To do: create letter fonts
const byte NumberFont[10][8] PROGMEM = {
{ 0x00, 0x1c, 0x22, 0x26, 0x2a, 0x32, 0x22, 0x1c },
{ 0x00, 0x1c, 0x08, 0x08, 0x08, 0x08, 0x0c, 0x08 },
{ 0x00, 0x3e, 0x02, 0x04, 0x18, 0x20, 0x22, 0x1c },
{ 0x00, 0x1c, 0x22, 0x20, 0x18, 0x20, 0x22, 0x1c },
{ 0x00, 0x10, 0x10, 0x3e, 0x12, 0x14, 0x18, 0x10 },
{ 0x00, 0x1c, 0x22, 0x20, 0x20, 0x1e, 0x02, 0x3e },
{ 0x00, 0x1c, 0x22, 0x22, 0x1e, 0x02, 0x04, 0x18 },
{ 0x00, 0x04, 0x04, 0x04, 0x08, 0x10, 0x20, 0x3e },
{ 0x00, 0x1c, 0x22, 0x22, 0x1c, 0x22, 0x22, 0x1c },
{ 0x00, 0x0c, 0x10, 0x20, 0x3c, 0x22, 0x22, 0x1c }
};
// "Tiny Tetris" upside-down text binarized from http://www.dcode.fr/binary-image
const byte welcomeScreen[16][5] PROGMEM = {
B01110011, B10100010, B00100011, B11100010, B00000000,
B10001001, B00100010, B00100000, B00100010, B00000000,
B10000001, B00100010, B00100000, B00100010, B00000000,
B01110001, B00011110, B00100001, B11100010, B00000000,
B00001001, B00100010, B00100000, B00100010, B00000000,
B10001001, B00100010, B00100000, B00100010, B00000000,
B01110011, B10011110, B11111011, B11101111, B10000000,
B00000000, B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000, B00000000,
B00000000, B10001000, B10111000, B10000000, B00000000,
B00000000, B10001100, B10010000, B10000000, B00000000,
B00000000, B10001100, B10010000, B10000000, B00000000,
B00000001, B01001010, B10010000, B10000000, B00000000,
B00000010, B00101001, B10010000, B10000000, B00000000,
B00000010, B00101001, B10010000, B10000000, B00000000,
B00000010, B00101000, B10111011, B11100000, B00000000
};
// Tetris Illustration upside-down image binarized from http://www.dcode.fr/binary-image
const byte tetrisLogo[40][8] PROGMEM = {
B11111111, B11111111, B11111111, B11111111, B11111111, B11111111, B11111111, B11111111,
B11101101, B10111111, B11111111, B11111111, B11111111, B01111111, B11111001, B11100111,
B11101101, B00110100, B11111111, B11111111, B11111110, B01110011, B11110001, B11100111,
B10111000, B01010101, B11111111, B11111111, B11111000, B01110011, B11100001, B11100111,
B10011110, B10110011, B10110011, B11100011, B11100100, B00100011, B11100011, B11110011,
B10001111, B00010011, B00110001, B11110001, B11110100, B00100011, B11100011, B11110011,
B10001111, B00000111, B01110001, B11110000, B11110010, B00110011, B11100011, B11110001,
B10001111, B00000110, B01100001, B11111000, B11111010, B00000001, B11000001, B11100001,
B10000110, B00001110, B11100000, B11111000, B01111001, B00000001, B11000000, B11000001,
B10000110, B00001100, B11100000, B11111100, B01111001, B00000001, B11000000, B00000001,
B10000110, B00001100, B11110000, B11111100, B01111001, B00000000, B10000000, B00000001,
B10000110, B00001100, B11110000, B01111100, B01111001, B00000000, B10000000, B00000001,
B10000110, B00000110, B11110000, B01111100, B01111001, B00000000, B10000000, B00000001,
B10000110, B00000111, B01111000, B01111000, B01110010, B00000000, B10000000, B00000001,
B10001101, B00000011, B00111000, B01111000, B01110010, B00000000, B00000000, B00000001,
B10011001, B10000011, B10111000, B01111000, B11110100, B00000000, B00000000, B00000001,
B10011001, B10000001, B10011100, B01110001, B11101100, B00000000, B00000000, B00000001,
B10001001, B00000000, B11111100, B01110001, B11011000, B00000000, B00000000, B00000001,
B10001011, B00000000, B01111100, B01100011, B10110000, B00000000, B00000000, B00000001,
B10000110, B00000000, B00110100, B11100111, B01100000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00011110, B11100110, B01000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00001110, B11001100, B10000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000110, B11011011, B00000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000111, B11010010, B00000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000011, B10100100, B00000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000001, B11111000, B00000000, B00000000, B00110000, B00000001,
B10000000, B00000000, B00000000, B11110000, B00000000, B00000000, B00110000, B00000001,
B10000000, B00000000, B00000000, B11010000, B00000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000000, B01110000, B00000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B10000000, B01100000, B00000000, B00000000, B00000000, B00000001,
B10000011, B00000000, B00000000, B01100000, B00000000, B00000000, B00000000, B00000001,
B10000011, B00000000, B00000000, B01100000, B00000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000000, B01100000, B00000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000000, B01100000, B00000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000000, B11110000, B00000000, B00000000, B00000000, B00010001,
B10000000, B00000000, B00000000, B11001000, B00000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000001, B10001000, B00000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000001, B10001000, B00000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000000, B10010000, B00000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000000, B11110000, B00001000, B00000000, B00000000, B00000001
};
// Tetris Brick upside-down image binarized from http://www.dcode.fr/binary-image
const byte brickLogo[36][8] PROGMEM= {
B10000000, B00000000, B00000000, B00000000, B00000000, B11111111, B11111100, B00000001,
B10000000, B00000111, B11111100, B11111111, B11111110, B11111111, B11111100, B00000001,
B10000011, B11111111, B11111110, B11111111, B11111111, B01111111, B11111110, B00000001,
B10000011, B11111111, B11111110, B01111111, B11111111, B00111111, B11111111, B00000001,
B10000011, B11111111, B11111111, B01111111, B11111111, B10111111, B11111111, B10000001,
B10001001, B11111111, B11111111, B00111111, B11111111, B10011111, B11111111, B10000001,
B10001101, B11111111, B11111111, B10111111, B11111111, B11001111, B11111111, B11000001,
B10001101, B11111111, B11111111, B10011111, B11111111, B11101111, B11111111, B11100001,
B10001100, B11111111, B11111111, B11011111, B11111111, B11100111, B11111111, B11110001,
B10001110, B11111111, B11111111, B11001111, B11111111, B11110111, B11111111, B11110001,
B10001110, B11111111, B11111111, B11101111, B11111111, B11111011, B11111111, B00000001,
B10001110, B01111111, B11111111, B11101111, B11111111, B11100000, B00000000, B00010001,
B10001111, B01111111, B11111111, B11100100, B00000000, B00000001, B11111111, B11110001,
B10001111, B00111111, B10000000, B00000000, B00111111, B11111011, B11111111, B11110001,
B10011111, B00000000, B00000111, B11110111, B11111111, B11110011, B11111111, B11100001,
B10001111, B00111111, B11111111, B11100111, B11111111, B11110111, B11111111, B11000001,
B10001111, B00111111, B11111111, B11101111, B11111111, B11100111, B11111111, B11000001,
B10001111, B01111111, B11111111, B11101111, B11111111, B11101111, B11111111, B10000001,
B10001111, B01111111, B11111111, B11001111, B11111111, B11001111, B11111111, B10000001,
B10000111, B01111111, B11111111, B11011111, B11111111, B11011111, B11111111, B00000001,
B10000110, B01111111, B11111111, B11011111, B11111111, B11011111, B11111111, B00000001,
B10000110, B01111111, B11111111, B10011111, B11111111, B10111111, B11111110, B00000001,
B10000010, B11111111, B11111111, B10111111, B11111111, B10111111, B11111000, B00000001,
B10000010, B11111111, B11111111, B10111111, B11111111, B00110000, B00000000, B00000001,
B10000010, B11111111, B11111111, B00111111, B11100000, B00000000, B00000000, B00000001,
B10000000, B11111111, B11111111, B00000000, B00000110, B00000000, B00000000, B00000001,
B10000000, B11111111, B11000000, B00000111, B11111110, B00000000, B00000000, B00000001,
B10000000, B10000000, B00001110, B01111111, B11111100, B00000000, B00000000, B00000001,
B10000000, B00000000, B00111110, B11111111, B11111100, B00000000, B00000000, B00000001,
B10000000, B00000000, B00011110, B11111111, B11111100, B00000000, B00000000, B00000001,
B10000000, B00000000, B00011100, B11111111, B11111000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00011101, B11111111, B11111000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00001101, B11111111, B11110000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00001001, B11111111, B11110000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000011, B11111111, B11100000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000011, B11110000, B00000000, B00000000, B00000000, B00000001
};
#define KEY_MIDDLE 0
#define KEY_LEFT 1
#define KEY_RIGHT 2
#define KEY_DOWN 3
#define KEY_ROTATE 4
#define PIEZO_PIN 3
#define LED_PIN 13
struct PieceSpace {
byte umBlock[4][4];
char Row;
char Coloum;
};
//Globals, is a mess. To do: tidy up and reduce glogal use if possible
byte pageArray[8] = { 0 };
byte scoreDisplayBuffer[8][6] = { { 0 }, { 0 } };
byte nextBlockBuffer[8][2] = { { 0 }, { 0 } };
bool optomizePageArray[8] = { 0 };
byte blockColoum[10] = { 0 };
byte tetrisScreen[14][25] = { { 1 } , { 1 } };
PieceSpace currentPiece = { 0 };
PieceSpace oldPiece = { 0 };
byte nextPiece = 0;
//keyPress key = { 0 };
bool gameOver = false;
unsigned long moveTime = 0;
int pageStart = 0;
int pageEnd = 0;
int score = 0;
int acceleration = 0;
int level = 0;
int levellineCount = 0;
int dropDelay = 1000;
int lastKey = 0;
// I2C
void OLEDCommand(byte command) {
Wire.beginTransmission(OLED_ADDRESS);
Wire.write(OLED_COMMAND);
Wire.write(command);
Wire.endTransmission();
}
void OLEDData(byte data) {
Wire.beginTransmission(OLED_ADDRESS);
Wire.write(OLED_DATA);
Wire.write(data);
Wire.endTransmission();
}
void setup() {
Serial.begin(9600);
while (!Serial);
Wire.begin();
Wire.setClock(400000);
pinMode(PIEZO_PIN, OUTPUT);
pinMode(LED_PIN, OUTPUT);
OLEDCommand(OLED_DISPLAY_OFF);
delay(20);
OLEDCommand(OLED_DISPLAY_ON);
delay(20);
OLEDCommand(OLED_NORMAL_DISPLAY);
delay(20);
OLEDCommand(0x8D);
delay(20);
OLEDCommand(0x14);
delay(20);
OLEDCommand(OLED_NORMAL_DISPLAY);
fillTetrisScreen(0);
randomSeed(analogRead(7)); /// To do: create a decent random number generator.
pinMode(JoyX, INPUT);
pinMode(JoyY, INPUT);
pinMode(Button, INPUT_PULLUP);
// blink led
digitalWrite(LED_PIN, HIGH);
delay(100);
digitalWrite(LED_PIN, LOW);
delay(200);
digitalWrite(LED_PIN, HIGH);
delay(50);
digitalWrite(LED_PIN, LOW);
}
void fillTetrisArray(byte value) {
for (char r = 0; r < 24; r++) {
for (char c = 0; c < 14; c++) {
tetrisScreen[c][r] = value;
}
}
for (char r = 21; r < 24; r++) {
for (char c = 0; c < 14; c++) {
tetrisScreen[c][r] = 0;
}
}
}
void fillTetrisScreen(byte value) {
for (int r = 1; r < 21; r++) {
for (int c = 2; c < 12; c++) {
tetrisScreen[c][r] = value;
}
}
}
void drawTetrisScreen() {
for (byte r = 1; r < 21; r++) {
//loop through rows to see if there is data to be sent
for (byte c = 2; c < 12; c++) {
if ((tetrisScreen[c][r] == 2) | (tetrisScreen[c][r] == 3)) {
//send line to screen
for (byte i = 0; i < 10; i++) {
blockColoum[i] = tetrisScreen[i + 2][r];
//clear delete block
if (tetrisScreen[i + 2][r] == 3) tetrisScreen[i + 2][r] = 0;
}
drawTetrisLine((r - 1) * 6);
break; break;
}
}
}
}
void drawTetrisTitle(bool blank = false) {
byte byteval;
//set Vertical addressing mode and column - page start end
OLEDCommand(OLED_SET_ADDRESSING);
OLEDCommand(OLED_VERTICAL_ADDRESSING);
OLEDCommand(OLED_SET_COLUMN);
OLEDCommand( 50 ); //Set column start
OLEDCommand( 66 ); //Set column end
OLEDCommand(OLED_SET_PAGE);
OLEDCommand( 1 ); //Set page start
OLEDCommand( 5 ); //Set page end
for (int r = 0; r <16; r++) {
for (int c = 4; c >=0; c--) {
if(blank) {
OLEDData(0);
}else {
byteval = pgm_read_byte(&welcomeScreen[r][c]);
OLEDData(byteval);
}
}
}
OLEDCommand(OLED_SET_COLUMN);
OLEDCommand( 1 ); //Set column start
OLEDCommand( 42 ); //Set column end
OLEDCommand(OLED_SET_PAGE);
OLEDCommand( 0 ); //Set page start
OLEDCommand( 7 ); //Set page end
for (int r = 0; r <40; r++) {
for (int c = 7; c >=0; c--) {
if(blank) {
OLEDData(0);
}else {
byteval = pgm_read_byte(&tetrisLogo[r][c]);
OLEDData(byteval);
}
}
}
OLEDCommand(OLED_SET_COLUMN);
OLEDCommand( 75 ); //Set column start
OLEDCommand( 116 ); //Set column end
OLEDCommand(OLED_SET_PAGE);
OLEDCommand( 0 ); //Set page start
OLEDCommand( 7 ); //Set page end
for (int r = 0; r <36; r++) {
for (int c = 7; c >=0; c--) {
if(blank) {
OLEDData(0);
}else {
byteval = pgm_read_byte(&brickLogo[r][c]);
OLEDData(byteval);
}
}
}
//brickLogo[36][8]
}
void drawTetrisLine(byte x) {
//fill array with blocks based on blockRow
//clear page and Optimize array
memset(optomizePageArray, 0, 8); ///review this... declare them here? interesting question...
memset(pageArray, 0, 8);
x++; // up one
//*********Column 0***********
//draw block
if (blockColoum[0] == 2 | blockColoum[0] == 1) {
pageArray[0] = pageArray[0] | B11111001;
optomizePageArray[0] = 1;
}
//delete block
if (blockColoum[0] == 3) {
pageArray[0] = pageArray[0] | B00000001; //create side wall
pageArray[0] = pageArray[0] & B00000111;
optomizePageArray[0] = 1;
}
//*********Column 1***********
if (blockColoum[1] == 2 | blockColoum[1] == 1) {
pageArray[1] = pageArray[1] | B00111110;
optomizePageArray[1] = 1;
}
//delete block
if (blockColoum[1] == 3) {
pageArray[1] = pageArray[1] & B11000001;
optomizePageArray[1] = 1;
}
//*********Column 2***********
if (blockColoum[2] == 2 | blockColoum[2] == 1) {
pageArray[1] = pageArray[1] | B10000000;
optomizePageArray[1] = 1;
pageArray[2] = pageArray[2] | B00001111;
optomizePageArray[2] = 1;
}
//delete block
if (blockColoum[2] == 3) {
pageArray[1] = pageArray[1] & B01111111;
optomizePageArray[1] = 1;
pageArray[2] = pageArray[2] & B11110000;
optomizePageArray[2] = 1;
}
//*********Column 3***********
if (blockColoum[3] == 2 | blockColoum[3] == 1) {
pageArray[2] = pageArray[2] | B11100000;
optomizePageArray[2] = 1;
pageArray[3] = pageArray[3] | B00000011;
optomizePageArray[3] = 1;
}
//delete block
if (blockColoum[3] == 3) {
pageArray[2] = pageArray[2] & B00011111;
optomizePageArray[2] = 1;
pageArray[3] = pageArray[3] & B11111100;
optomizePageArray[3] = 1;
}
//*********Column 4***********
if (blockColoum[4] == 2 | blockColoum[4] == 1) {
pageArray[3] = pageArray[3] | B11111000;
optomizePageArray[3] = 1;
}
//delete block
if (blockColoum[4] == 3) {
pageArray[3] = pageArray[3] & B00000111;
optomizePageArray[3] = 1;
}
//*********Column 5***********
if (blockColoum[5] == 2 | blockColoum[5] == 1) {
pageArray[4] = pageArray[4] | B00111110;
optomizePageArray[4] = 1;
}
//delete block
if (blockColoum[5] == 3) {
pageArray[4] = pageArray[4] & B11000001;
optomizePageArray[4] = 1;
}
//*********Column 6***********
if (blockColoum[6] == 2 | blockColoum[6] == 1) {
pageArray[4] = pageArray[4] | B10000000;
optomizePageArray[4] = 1;
pageArray[5] = pageArray[5] | B00001111;
optomizePageArray[5] = 1;
}
//delete block
if (blockColoum[6] == 3) {
pageArray[4] = pageArray[4] & B01111111;
optomizePageArray[4] = 1;
pageArray[5] = pageArray[5] & B11110000;
optomizePageArray[5] = 1;
}
//*********Column 7***********
if (blockColoum[7] == 2 | blockColoum[7] == 1) {
pageArray[5] = pageArray[5] | B11100000;
optomizePageArray[5] = 1;
pageArray[6] = pageArray[6] | B00000011;
optomizePageArray[6] = 1;
}
if (blockColoum[7] == 3) {
pageArray[5] = pageArray[5] & B00011111;
optomizePageArray[5] = 1;
pageArray[6] = pageArray[6] & B11111100;
optomizePageArray[6] = 1;
}
//*********Column 8***********
if (blockColoum[8] == 2 | blockColoum[8] == 1) {
pageArray[6] = pageArray[6] | B11111000;
optomizePageArray[6] = 1;
}
//delete block
if (blockColoum[8] == 3) {
pageArray[6] = pageArray[6] & B00000111;
optomizePageArray[6] = 1;
}
//*********Column 9***********
if (blockColoum[9] == 2 | blockColoum[9] == 1) {
pageArray[7] = pageArray[7] | B10111110;
optomizePageArray[7] = 1;
}
if (blockColoum[9] == 3) {
pageArray[7] = pageArray[7] | B10000000;//create side wall
pageArray[7] = pageArray[7] & B11000001;
optomizePageArray[7] = 1;
}
//Optimize - figure out what page array has data
for (int page = 0; page < 8; page++) {
if (optomizePageArray[page]) {
//block found set page start
pageStart = page;
break;
}
}
for (int page = 7; page >= 0; page--) {
if (optomizePageArray[page]) {
//block found set page end
pageEnd = page;
break;
}
}
//set Vertical addressing mode and column - page start end
OLEDCommand(OLED_SET_ADDRESSING);
OLEDCommand(OLED_VERTICAL_ADDRESSING);
OLEDCommand(OLED_SET_COLUMN);
OLEDCommand(x);
OLEDCommand(x + 4);
OLEDCommand(OLED_SET_PAGE);
OLEDCommand(pageStart);
OLEDCommand(pageEnd);
//send the array 5 times
for (int c = 0; c < 5; c++) {
for (int p = pageStart; p <= pageEnd; p++) {
OLEDData(pageArray[p]);
}
}
}
void loadPiece(byte pieceNumber, byte row, byte coloum, bool loadScreen) {
//load the piece from piece array to screen
byte pieceRow = 0;
byte pieceColoum = 0;
byte c = 0;
// load piece from progmem
byte byte_in;
bool piece_out[4][4];
byte piece_bit[2] = {0,0};
for(int i=0;i<2;i++) {
byte_in = pgm_read_byte(&Blocks[pieceNumber-1][i]);
for( byte mask = 1; mask; mask <<=1) {
if(mask & byte_in) {
piece_out[piece_bit[0]][piece_bit[1]] = 1;
} else {
piece_out[piece_bit[0]][piece_bit[1]] = 0;
}
piece_bit[1]++;
if(piece_bit[1]>=4) {
piece_bit[1]=0;
piece_bit[0]++;
}
}
}
memcpy(currentPiece.umBlock, piece_out, 16);
currentPiece.Row = row;
currentPiece.Coloum = coloum;
if (loadScreen) {
oldPiece = currentPiece;
for (c = coloum; c < coloum + 4; c++) {
for (int r = row; r < row + 4; r++) {
if (currentPiece.umBlock[pieceColoum][pieceRow]) tetrisScreen[c][r] = 2;
pieceRow++;
}
pieceRow = 0;
pieceColoum++;
}
}
}
void drawPiece() {
char coloum;
char row;
byte pieceRow = 0;
byte pieceColoum = 0;
char c = 0;
// delete blocks first
coloum = oldPiece.Coloum;
row = oldPiece.Row;
for (c = coloum; c < coloum + 4; c++) {
for (char r = row; r < row + 4; r++) {
if (oldPiece.umBlock[pieceColoum][pieceRow]) tetrisScreen[c][r] = 3;
pieceRow++;
}
pieceRow = 0;
pieceColoum++;
}
//draw new blocks
pieceRow = 0;
pieceColoum = 0;
c = 0;
coloum = currentPiece.Coloum;
row = currentPiece.Row;
for (c = coloum; c < coloum + 4; c++) {
for (char r = row; r < row + 4; r++) {
if (currentPiece.umBlock[pieceColoum][pieceRow]) tetrisScreen[c][r] = 2;
pieceRow++;
}
pieceRow = 0;
pieceColoum++;
}
}
void drawLandedPiece() {
char coloum;
char row;
byte pieceRow = 0;
byte pieceColoum = 0;
char c = 0;
// Landed pieces are 1
coloum = currentPiece.Coloum;
row = currentPiece.Row;
for (c = coloum; c < coloum + 4; c++) {
for (int r = row; r < row + 4; r++) {
if (currentPiece.umBlock[pieceColoum][pieceRow]) tetrisScreen[c][r] = 1;
pieceRow++;
}
pieceRow = 0;
pieceColoum++;
}
processCompletedLines();
}
bool led = true;
void RotatePiece() {
byte i, j;
byte umFig[4][4] = { 0 };
memcpy(oldPiece.umBlock, currentPiece.umBlock, 16);
oldPiece.Row = currentPiece.Row;
oldPiece.Coloum = currentPiece.Coloum;
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
umFig[j][i] = currentPiece.umBlock[4 - i - 1][j];
}
}
oldPiece = currentPiece;
memcpy(currentPiece.umBlock, umFig, 16);
if (checkColloision()) currentPiece = oldPiece;
// no need for this...
if (led) {
digitalWrite(LED_PIN, HIGH);
led = false;
}
delay(1);
digitalWrite(LED_PIN, LOW);
if (led == false) {
digitalWrite(LED_PIN, LOW);
led = true;
}
}
bool movePieceDown() {
bool pieceLanded = false;
char rndPiece = 0;
oldPiece = currentPiece;
currentPiece.Row = currentPiece.Row - 1;
//check collision
if (checkColloision()) {
// its at the bottom make it a landed piece and start new piece
currentPiece = oldPiece; // back to where it was
drawLandedPiece();
pieceLanded = true;
}
if (pieceLanded) {
loadPiece(nextPiece, 19, 4, false);
acceleration = 0;
if (checkColloision()) {
gameOver = true;
} else {
loadPiece(nextPiece, 19, 4, true);
acceleration = 0;//reset acceleration as there is a new piece
}
nextPiece = random(1, 8);
setNextBlock(nextPiece);
}
}
void movePieceLeft() {
oldPiece = currentPiece;
currentPiece.Coloum = currentPiece.Coloum - 1;
//check collision
if (checkColloision()) {
currentPiece = oldPiece; // back to where it was
}
}
void movePieceRight() {
oldPiece = currentPiece;
currentPiece.Coloum = currentPiece.Coloum + 1;
//check collision
if (checkColloision()) {
currentPiece = oldPiece; // back to where it was
}
}
bool checkColloision() {
byte pieceRow = 0;
byte pieceColoum = 0;
char c = 0;
char coloum = currentPiece.Coloum;
char row = currentPiece.Row;
//scan across piece and translate to Tetris array and check Collisions.
for (c = coloum; c < coloum + 4; c++) {
for (char r = row; r < row + 4; r++) {
if (currentPiece.umBlock[pieceColoum][pieceRow]) {
if (tetrisScreen[c][r] == 1) return true; //is it on landed blocks?
}
pieceRow++;
}
pieceRow = 0;
pieceColoum++;
}
return false;
}
void processCompletedLines() {
char rowCheck = 0;
char coloumCheck = 0;
bool fullLine = false;
bool noLine = true;
char linesProcessed = 0;
char clearedLines = 0;
char topRow = 0;
char bottomRow = 0;
char currentRow = 0;
int amountScored = 0;
if (currentPiece.Row < 1)bottomRow = 1;
else bottomRow = currentPiece.Row;
for (int rowCheck = bottomRow; rowCheck < currentPiece.Row + 4; rowCheck++) {
bool fullLine = true;
for (coloumCheck = 2; coloumCheck < 12; coloumCheck++) {
if (tetrisScreen[coloumCheck][rowCheck] == 0) {
fullLine = false;
break;
}
}
if (fullLine) {
//make line values 3's and render
for (char c = 2; c < 12; c++) {
tetrisScreen[c][rowCheck] = 3;
}
bottomRow = rowCheck + 1;
//line is now all 0's
linesProcessed++;
delay(77); // animation :)
}
drawTetrisScreen();
}
//******all lines are 0's and have been removed from the screen
if (linesProcessed) {
clearedLines = linesProcessed;
while (clearedLines) {
for (currentRow = 1; currentRow < 20; currentRow++) {
noLine = true;
for (char c = 2; c < 12; c++) {
if (tetrisScreen[c][currentRow]) noLine = false;
}
if (noLine) {
//move all lines down
for (int r = currentRow + 1; r < 20; r++) {
for (char c = 2; c < 12; c++) {
if (tetrisScreen[c][r]) tetrisScreen[c][r - 1] = 2;
else tetrisScreen[c][r - 1] = 3;
}
}
}
}
//make the 2's 1's
for (char r = 1; r < 24; r++) {
for (char c = 2; c < 12; c++) {
if (tetrisScreen[c][r] == 2)tetrisScreen[c][r] = 1;
}
}
clearedLines--;
drawTetrisScreen();
tone(PIEZO_PIN, 1000, 50);
delay(60);
tone(PIEZO_PIN, 2000, 50);
delay(50);
tone(PIEZO_PIN, 500, 50);
delay(60);
}
}
// ************** process score *******************
switch (linesProcessed) {
case 1: amountScored = 40 * (level + 1); break;
case 2: amountScored = 100 * (level + 1); break;
case 3: amountScored = 300 * (level + 1); break;
case 4: amountScored = 1200 * (level + 1);
//do 4 line affect
OLEDCommand(OLED_INVERSE_DISPLAY);
delay(20);
OLEDCommand(OLED_NORMAL_DISPLAY);
break;
}
//score animation
for (long s = score; s < score + amountScored; s = s + (5 * (level + 1))) {
setScore(s, false);
}
score = score + amountScored;
setScore(score, false);
//****update level line count
levellineCount = levellineCount + linesProcessed;
if (levellineCount > 10) {
level++;
levellineCount = 0;
//do level up affect
OLEDCommand(OLED_INVERSE_DISPLAY);
delay(100);
OLEDCommand(OLED_NORMAL_DISPLAY);
for (int i = 250; i < 2500; i += 100) {
tone(PIEZO_PIN, i, 5);
delay(5);
tone(PIEZO_PIN, i / 2, 5);
delay(10);
}
OLEDCommand(OLED_INVERSE_DISPLAY);
delay(100);
OLEDCommand(OLED_NORMAL_DISPLAY);
}
//make the 2's 1's
for (char r = bottomRow; r <= topRow; r++) {
for (char c = 2; c < 12; c++) {
if (tetrisScreen[c][r]) {
tetrisScreen[c][r] = 1;
}
}
}
}
void tetrisScreenToSerial() {
//for debug
for (int r = 0; r < 24; r++) {
for (int c = 0; c < 14; c++) {
Serial.print(tetrisScreen[c][r], DEC);
}
Serial.println();
}
Serial.println();
}
bool processKeys() {
bool keypressed = true;
int leftRight = 300 - acceleration;
int rotate = 700;
int down = 110 - acceleration;
int dpadpos = Dpad::getPos();
//Serial.println(dpadpos);
switch(dpadpos) {
case KEY_LEFT:
if( Dpad::DoDebounce() ) {
acceleration = Dpad::setAccel(acceleration, leftRight);
}
movePieceLeft();
break;
case KEY_RIGHT:
if( Dpad::DoDebounce() ) {
acceleration = Dpad::setAccel(acceleration, leftRight);
}
movePieceRight();
break;
case KEY_DOWN:
if( Dpad::DoDebounce() ) {
acceleration = Dpad::setAccel(acceleration, down);
}
movePieceDown();
break;
case KEY_ROTATE:
if( Dpad::DoDebounce() ) {
acceleration = Dpad::setAccel(acceleration, rotate);
}
RotatePiece();
break;
default:
acceleration = 0;
processKey = true;
Debounce = 0;
keypressed = false;
break;
}
if (keypressed) {
drawPiece();
drawTetrisScreen();
}
}
void setScore(long score, bool blank)
{
// this is a kludge. To do: create a proper system for rendering numbers and letters.
long ones = (score % 10);
long tens = ((score / 10) % 10);
long hundreds = ((score / 100) % 10);
long thousands = ((score / 1000) % 10);
long tenthousands = ((score / 10000) % 10);
long hunderedthousands = ((score / 100000) % 10);
//create the score in upper left part of the screen
byte font = 0;
char bytes_out[8];
memset(scoreDisplayBuffer, 0, sizeof scoreDisplayBuffer);
//****************score digit 6****************
for (int v = 0; v<8; v++) bytes_out[v] = pgm_read_byte(&NumberFont[hunderedthousands][v]);
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][0] = scoreDisplayBuffer[i][0] | bytes_out[i] >> 1;
}
//****************score digit 5****************
for (int v = 0; v<8; v++) bytes_out[v] = pgm_read_byte(&NumberFont[tenthousands][v]);
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][0] = scoreDisplayBuffer[i][0] | (bytes_out[i] << 6);
}
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][1] = scoreDisplayBuffer[i][1] | bytes_out[i] >> 1;
}
//****************score digit 4****************
for (int v = 0; v<8; v++) bytes_out[v] = pgm_read_byte(&NumberFont[thousands][v]);
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][1] = scoreDisplayBuffer[i][1] | (bytes_out[i] << 6);
}
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][2] = scoreDisplayBuffer[i][2] | bytes_out[i] >> 1;
}
//****************score digit 3****************
for (int v = 0; v<8; v++) bytes_out[v] = pgm_read_byte(&NumberFont[hundreds][v]);
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][2] = scoreDisplayBuffer[i][2] | (bytes_out[i] << 6);
}
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][3] = scoreDisplayBuffer[i][3] | bytes_out[i] >> 1;
}
//****************score digit 2****************
for (int v = 0; v<8; v++) bytes_out[v] = pgm_read_byte(&NumberFont[tens][v]);
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][3] = scoreDisplayBuffer[i][3] | (bytes_out[i] << 6);
}
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][4] = scoreDisplayBuffer[i][4] | bytes_out[i] >> 1;
}
//****************score digit 1****************
for (int v = 0; v<8; v++) bytes_out[v] = pgm_read_byte(&NumberFont[ones][v]);
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][4] = scoreDisplayBuffer[i][4] | (bytes_out[i] << 6);
}
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][5] = scoreDisplayBuffer[i][5] | bytes_out[i] >> 1;
}
//set Vertical addressing mode and column - page start end
OLEDCommand(OLED_SET_ADDRESSING);
OLEDCommand(OLED_VERTICAL_ADDRESSING);
OLEDCommand(OLED_SET_COLUMN);
OLEDCommand(120); //Set column start
OLEDCommand(127); //Set column end
OLEDCommand(OLED_SET_PAGE);
OLEDCommand(0); //Set page start
OLEDCommand(5); //Set page end
for (int p = 0; p < 8; p++)
{
for (int c = 0; c <6; c++)
{
if (blank) OLEDData(0);
else OLEDData(scoreDisplayBuffer[p][c]);
}
}
}
void setNextBlock(byte pieceNumber) {
memset(nextBlockBuffer, 0, sizeof nextBlockBuffer); //clear buffer
switch (pieceNumber) {
case 1:
//************l piece - 1 *************
for (int k = 2; k < 6; k++) {
nextBlockBuffer[k][0] = B01110111;
nextBlockBuffer[k][1] = B01110111;
}
break;
case 2:
//************J piece - 2 *************
for (int k = 0; k < 3; k++) {
nextBlockBuffer[k][0] = B01110000;
nextBlockBuffer[k][1] = B01110111;
}
for (int k = 4; k < 7; k++) {
nextBlockBuffer[k][0] = B01110000;
}
break;
case 3:
//************L piece - 3 *************
for (int k = 0; k < 3; k++) {
nextBlockBuffer[k][0] = B01110000;
}
for (int k = 4; k < 7; k++) {
nextBlockBuffer[k][0] = B01110000;
nextBlockBuffer[k][1] = B01110111;
}
break;
case 4:
//************O piece - 4 *************
for (int k = 0; k < 3; k++) {
nextBlockBuffer[k][0] = B01110000;
nextBlockBuffer[k][1] = B00000111;
}
for (int k = 4; k < 7; k++) {
nextBlockBuffer[k][0] = B01110000;
nextBlockBuffer[k][1] = B00000111;
}
break;
case 5:
//************S piece - 5 *************
for (int k = 0; k < 3; k++) {
nextBlockBuffer[k][0] = B01110000;
nextBlockBuffer[k][1] = B00000111;
}
for (int k = 4; k < 7; k++) {
nextBlockBuffer[k][0] = B00000000;
nextBlockBuffer[k][1] = B11101110;
}
break;
case 6:
//************T piece - 6 *************
for (int k = 0; k < 3; k++) {
nextBlockBuffer[k][0] = B01110000;
nextBlockBuffer[k][1] = B01110111;
}
for (int k = 4; k < 7; k++) {
nextBlockBuffer[k][0] = B00000000;
nextBlockBuffer[k][1] = B00001110;
}
break;
case 7:
//************Z piece - 7 *************
for (int k = 0; k < 3; k++) {
nextBlockBuffer[k][0] = B01110000;
nextBlockBuffer[k][1] = B00000111;
}
for (int k = 4; k < 7; k++) {
nextBlockBuffer[k][0] = B11101110;
nextBlockBuffer[k][1] = B00000000;
}
break;
}
//set Vertical addressing mode and column - page start end
OLEDCommand(OLED_SET_ADDRESSING);
OLEDCommand(OLED_VERTICAL_ADDRESSING);
OLEDCommand(OLED_SET_COLUMN);
OLEDCommand(120); //Set column start
OLEDCommand(127); //Set column end
OLEDCommand(OLED_SET_PAGE);
OLEDCommand(6); //Set page start
OLEDCommand(7); //Set page end
for (int p = 0; p < 8; p++) {
for (int c = 0; c < 2; c++) {
OLEDData(nextBlockBuffer[p][c]);
}
}
}
void drawBottom() {
//set Vertical addressing mode and column - page start end
OLEDCommand(OLED_SET_ADDRESSING);
OLEDCommand(OLED_VERTICAL_ADDRESSING);
OLEDCommand(OLED_SET_COLUMN);
OLEDCommand(0); //Set column start
OLEDCommand(0); //Set column end
OLEDCommand(OLED_SET_PAGE);
OLEDCommand(0); //Set page start
OLEDCommand(7); //Set page end
for (int c = 0; c < 8; c++) {
OLEDData(255);
}
}
void drawSides() {
//set Vertical addressing mode and column - page start end
OLEDCommand(OLED_SET_ADDRESSING);
OLEDCommand(OLED_VERTICAL_ADDRESSING);
OLEDCommand(OLED_SET_COLUMN);
OLEDCommand(0); //Set column start
OLEDCommand(127); //Set column end
OLEDCommand(OLED_SET_PAGE);
OLEDCommand(0); //Set page start
OLEDCommand(7); //Set page end
for (int r = 0; r < 128; r++) {
for (int c = 0; c < 8; c++) {
if (c == 0) OLEDData(1);
else if (c == 7) OLEDData(128);
else OLEDData(0);
}
}
}
void loop() {
//main loop code
//To do: create high score system that savees to EEprom
gameOver = false;
score = 0;
fillTetrisArray(1); //fill with 1's to make border
fillTetrisScreen(2);
drawTetrisScreen();
delay(200);
fillTetrisScreen(3);
drawTetrisScreen();
delay(200);
drawSides();
drawBottom();
// tetrisScreenToSerial();
OLEDCommand(OLED_INVERSE_DISPLAY);
delay(200);
OLEDCommand(OLED_NORMAL_DISPLAY);
loadPiece(random(1, 8), 20, 5, true);
drawTetrisScreen();
nextPiece = random(1, 8);
setNextBlock(nextPiece);
setScore(0, false);
delay(300);
setScore(0, true);
delay(300);
setScore(0, false);
byte rnd = 0;
drawTetrisTitle(false);
TetrisTheme::start();
while(songOn) TetrisTheme::tetrisThemePlay();
drawTetrisTitle(true);
drawSides();
drawBottom();
setScore(0, false);
for(int i=1;i<10;i++) {
nextPiece = random(1, 8);
setNextBlock(nextPiece);
delay(100);
}
while (!gameOver) {
movePieceDown();
drawPiece();
drawTetrisScreen();
moveTime = millis();
while (millis() - moveTime < (dropDelay - (level * 50))) {
processKeys();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.