Tetris game for mbed.
Dependencies: 4DGL-uLCD-SE SDFileSystem mbed-rtos mbed wave_player
Fork of HelloWorld by
tetris.cpp
- Committer:
- greiner218
- Date:
- 2016-11-07
- Revision:
- 2:36cda8980746
File content as of revision 2:36cda8980746:
#include <stdlib.h> #include <time.h> #include "tetris.h" #include "uLCD_4DGL.h" #include "rtos.h" #include "mbed.h" uLCD_4DGL uLCD(p28,p27,p29); // serial tx, serial rx, reset pin; Mutex lcd_mutex; Block::Block(){ srand(time(NULL)); block = block_list[rand() % SET_SIZE]; offset = DIMX / 2; height = DIMZ - 5; } Block::Block(short ind){ block = block_list[ind]; offset = DIMX / 2; height = DIMZ - 5; } Block::Block(short ind, short offs, short h){ block = block_list[ind]; offset = offs; height = h; } unsigned long long Block::getFace(int n){ unsigned long long rv = 0; unsigned int lower = block&0xFFFFFFFFULL; unsigned int upper = (block>>32); switch(n){ case 0: rv = lower&FACE0; break; case 1: rv = (lower>>16)&FACE0;//shift(block,-16)&FACE0; break; case 2: rv = (upper)&FACE0;//shift(block,-16)&FACE0; break; case 3: rv = (upper>>16)&FACE0;//shift(block,-16)&FACE0; break; default: rv = 0; } return rv; } unsigned long long Block::getLine(int n){ unsigned long long rv = 0; unsigned int lower = block&0xFFFFFFFFULL; unsigned int upper = (block>>32); switch(n){ case 0: rv = (lower&LINE0) | ((upper&LINE0)<<32); break; case 1: lower = lower >>4; upper = upper >>4; rv = (lower&LINE0) | ((upper&LINE0)<<32); break; case 2: lower = lower >>8; upper = upper >>8; rv = (lower&LINE0) | ((upper&LINE0)<<32); break; case 3: lower = lower >>12; upper = upper >>12; rv = (lower&LINE0) | ((upper&LINE0)<<32); break; default: rv = 0; } return rv; } unsigned long long Block::getFaceLine(int f, int l){ unsigned long long face = getFace(f); unsigned long long rv = 0; switch(l){ case 0: rv = (face&0x000F); break; case 1: rv = (face&0x00F0)>>4; break; case 2: rv = (face&0x0F00)>>8; break; case 3: rv = (face&0xF000)>>12; break; default: rv = 0; } return rv; } void Block::rotY(){ unsigned long long temp = block; block = ((temp&0x0001000100010001ULL)<<3)|((temp&0x0002000200020002ULL)<<6)| ((temp&0x0004000400040004ULL)<<9)|((temp&0x0008000800080008ULL)<<12)| ((temp&0x0010001000100010ULL)>>2)|((temp&0x0020002000200020ULL)<<1)| ((temp&0x0040004000400040ULL)<<4)|((temp&0x0080008000800080ULL)<<7)| ((temp&0x0100010001000100ULL)>>7)|((temp&0x0200020002000200ULL)>>4)| ((temp&0x0400040004000400ULL)>>1)|((temp&0x0800080008000800ULL)<<2)| ((temp&0x1000100010001000ULL)>>12)|((temp&0x2000200020002000ULL)>>9)| ((temp&0x4000400040004000ULL)>>6)|((temp&0x8000800080008000ULL)>>3); } void Block::rotZ(){ unsigned long long temp = block; block = ((temp&0x0000000000001111ULL)<<3) |((temp&0x0000000000002222ULL)<<18)| ((temp&0x0000000000004444ULL)<<33)|((temp&0x0000000000008888ULL)<<48)| ((temp&0x0000000011110000ULL)>>14)|((temp&0x0000000022220000ULL)<<1) | ((temp&0x0000000044440000ULL)<<16)|((temp&0x0000000088880000ULL)<<31)| ((temp&0x0000111100000000ULL)>>31)|((temp&0x0000222200000000ULL)>>16)| ((temp&0x0000444400000000ULL)>>1) |((temp&0x0000888800000000ULL)<<14)| ((temp&0x1111000000000000ULL)>>48)|((temp&0x2222000000000000ULL)>>33)| ((temp&0x4444000000000000ULL)>>18)|((temp&0x8888000000000000ULL)>>3); } void Block::rotX(){ unsigned long long temp = block; block = ((temp&0x000000000000000FULL)<<12)|((temp&0x00000000000000F0ULL)<<24)| ((temp&0x0000000000000F00ULL)<<36)|((temp&0x000000000000F000ULL)<<48)| ((temp&0x00000000000F0000ULL)>>8)| ((temp&0x0000000000F00000ULL)<<4)| ((temp&0x000000000F000000ULL)<<16)|((temp&0x00000000F0000000ULL)<<28)| ((temp&0x0000000F00000000ULL)>>28)|((temp&0x000000F000000000ULL)>>16)| ((temp&0x00000F0000000000ULL)>>4) |((temp&0x0000F00000000000ULL)<<8)| ((temp&0x000F000000000000ULL)>>48)|((temp&0x00F0000000000000ULL)>>36)| ((temp&0x0F00000000000000ULL)>>24)|((temp&0xF000000000000000ULL)>>12); } void Block::movR(){ offset++; //if (offset >= DIMX) offset = 0; } void Block::movL(){ offset--; if (offset <-3) offset = -3; } void Block::movB(){ unsigned long long temp = block; block = ((temp&0x0000FFFFFFFFFFFFULL)<<16)|((temp&0xFFFF000000000000ULL)>>48); } void Block::movF(){ unsigned long long temp = block; block = ((temp&0xFFFFFFFFFFFF0000ULL)>>16)|((temp&0x000000000000FFFFULL)<<48); } void Block::movU(){ height++; } void Block::movD(){ height--; //if (height<-3) height = -3; } Board::Board(){ nextBlock.offset = activeBlock.offset; nextBlock.height = activeBlock.height; nextBlock.block = activeBlock.block; int i; for (i = 0; i<4; i++) line[i] = 0xFFFFFFFFFFFFFFFFULL; for (i = 4; i<DIMZ+4; i++) line[i] = 0xF807F807F807F807ULL; score = 0; } int Board::check(){ //Check next block with board lines. //Returns 0 when a collision (illegal state) is detected. //Returns 1 when move is legal. int invalid = 0; int f = 0; int l = 0; if (nextBlock.height >-4){ for (l=0;l<4;l++){ for (f=0;f<4;f++){ if (!checkFaceLine(f,l)) invalid = 1; } } } else{ invalid = 1; } if(invalid){ nextBlock.height = activeBlock.height; nextBlock.offset = activeBlock.offset; nextBlock.block = activeBlock.block; return 0; } return 1; } int Board::checkFaceLine(int f, int l){ //Determine which board line is being checked unsigned long long boardLine = line[nextBlock.height + l +4]; unsigned long long val = nextBlock.getFaceLine(f,l); //Determine which board values are being compared unsigned int lower = boardLine&0x00000000FFFFFFFF; unsigned int upper = boardLine >> 32; switch(f){ case 0: boardLine = lower&0x0000FFFF; break; case 1: boardLine = lower>>16; break; case 2: boardLine = upper&0x0000FFFF; break; case 3: boardLine = upper>>16; break; default: break; } //Shift block to align with board val = shift(val,3+nextBlock.offset); //boardLine = (boardLine>>2)<<nextBlock.offset; //Compare block line value with the board value if(boardLine&val)return 0;//Collision detected return 1; } void Board::updateBlock(){ eraseBlock(activeBlock); activeBlock = nextBlock; drawBlock(activeBlock); } void Board::checkAndUpdate(){ if(check()) updateBlock(); } void Board::setBlock(){ //Set block line by line. int f = 0; int l = 0; for (f=0; f<4; f++){ for (l=0; l<4; l++){ setBlockFaceLine(f,l); } } //Now check if lines need to be cleared for (l=activeBlock.height+3; (l>activeBlock.height-1); l--){ checkForClear(l+4); } //Now check if you lost checkLose(); } void Board::checkForClear(int l){ if(l>3){ if( line[l] == 0xFFFFFFFFFFFFFFFFULL) clearLine(l); } } void Board::setBlockFaceLine(int f, int l){ //Determine which board line is being set unsigned long long boardLine = getFaceLine(f,l+4+nextBlock.height); unsigned long long val = nextBlock.getFaceLine(f,l); //Shift block to align with board val = shift(val,3+nextBlock.offset); //Write to the board boardLine |= val; setBlockFaceLineVal(f,l+4+nextBlock.height, boardLine); } void Board::setBlockFaceLineVal(int f, int l, unsigned long long bl){ switch(f){ case 0: line[l] |= (bl); break; case 1: line[l] |= (bl<<16); break; case 2: line[l] |= (bl<<32); break; case 3: line[l] |= (bl<<48); break; default: break; } } void Board::drawGrid(){ uLCD.baudrate(3000000); int col = BLUE; if(lost) col = RED; lcd_mutex.lock(); //Top left uLCD.rectangle(127,0,126,91,col); uLCD.rectangle(93,0,92,91,col); //Bot right uLCD.rectangle(0,127,1,36,col); uLCD.rectangle(34,127,35,36,col); //Bot left uLCD.rectangle(0,0,93,1,col); uLCD.rectangle(0,34,93,35,col); //Top right uLCD.rectangle(127,127,34,126,col); uLCD.rectangle(127,93,34,92,col); drawBlock(activeBlock); drawScore(); lcd_mutex.unlock(); } void Board::drawPix(int line, int offset, int grid){ int x0; int y0; switch(grid){ case 0: x0 = 94; y0 = 36; uLCD.rectangle(y0 + 4*line, x0 + 4*offset, y0 + 4*line + 3, x0 + 4*offset + 3, RED); break; case 1: x0 = 36; y0 = 33; uLCD.rectangle(y0 - 4*offset, x0 + 4*line, y0 - 4*offset - 3, x0 + 4*line + 3, RED); break; case 2: x0 = 33; y0 = 91; uLCD.rectangle(y0 - 4*line, x0 - 4*offset, y0 - 4*line - 3, x0 - 4*offset - 3, RED); break; case 3: x0 = 91; y0 = 94; uLCD.rectangle(y0 + 4*offset, x0 - 4*line, y0 + 4*offset + 3, x0 - 4*line - 3, RED); break; default: break; } } void Board::erasePix(int line, int offset, int grid){ int x0; int y0; switch(grid){ case 0: x0 = 94; y0 = 36; uLCD.rectangle(y0 + 4*line, x0 + 4*offset, y0 + 4*line + 3, x0 + 4*offset + 3, BLACK); break; case 1: x0 = 36; y0 = 33; uLCD.rectangle(y0 - 4*offset, x0 + 4*line, y0 - 4*offset - 3, x0 + 4*line + 3, BLACK); break; case 2: x0 = 33; y0 = 91; uLCD.rectangle(y0 - 4*line, x0 - 4*offset, y0 - 4*line - 3, x0 - 4*offset - 3, BLACK); break; case 3: x0 = 91; y0 = 94; uLCD.rectangle(y0 + 4*offset, x0 - 4*line, y0 + 4*offset + 3, x0 - 4*line - 3, BLACK); break; default: break; } } void Board::drawBlock(Block blk){ unsigned long long temp; int i = 0; lcd_mutex.lock(); //Draw front face in grid 0 temp = (blk.block&FACE0); for (i=0; i<16; i++){ if(temp&(0x01<<i)) drawPix(blk.height+(i/4), blk.offset+(i%4),0); } //Draw second face in grid 1 temp = (blk.block&FACE1)>>16; for (i=0; i<16; i++){ if(temp&(0x01<<i)) drawPix(blk.height+(i/4), blk.offset+(i%4),1); } //Draw third face in grid 2 temp = (blk.block&FACE2)>>32; for (i=0; i<16; i++){ if(temp&(0x01<<i)) drawPix(blk.height+(i/4), blk.offset+(i%4),2); } //Draw fourth face in grid 3 temp = (blk.block&FACE3)>>48; for (i=0; i<16; i++){ if(temp&(0x01<<i)) drawPix(blk.height+(i/4), blk.offset+(i%4),3); } lcd_mutex.unlock(); } void Board::eraseBlock(Block blk){ unsigned long long temp; int i = 0; lcd_mutex.lock(); //Draw front face in grid 0 temp = (blk.block&FACE0); for (i=0; i<16; i++){ if(temp&(0x01<<i)) erasePix(blk.height+(i/4), blk.offset+(i%4),0); } //Draw second face in grid 1 temp = (blk.block&FACE1)>>16; for (i=0; i<16; i++){ if(temp&(0x01<<i)) erasePix(blk.height+(i/4), blk.offset+(i%4),1); } //Draw third face in grid 2 temp = (blk.block&FACE2)>>32; for (i=0; i<16; i++){ if(temp&(0x01<<i)) erasePix(blk.height+(i/4), blk.offset+(i%4),2); } //Draw fourth face in grid 3 temp = (blk.block&FACE3)>>48; for (i=0; i<16; i++){ if(temp&(0x01<<i)) erasePix(blk.height+(i/4), blk.offset+(i%4),3); } lcd_mutex.unlock(); } void Board::getNewBlock(){ activeBlock.block = block_list[rand() % SET_SIZE]; activeBlock.offset = DIMX / 2; activeBlock.height = DIMZ - 5; nextBlock.offset = activeBlock.offset; nextBlock.height = activeBlock.height; nextBlock.block = activeBlock.block; } unsigned long long shift(unsigned long long value,int offset){ unsigned long long rv; if (offset > 0) rv = (value<<offset); if (offset < 0) rv = (value>>offset); if (offset == 0) rv = value; return rv; } unsigned long long Board::getFaceLine(int f, int l){ unsigned int lower = line[l]&0xFFFFFFFFULL; unsigned int upper = (line[l]>>32); unsigned long long boardLine = 0; switch(f){ case 0: boardLine = lower&0xFFFF; break; case 1: boardLine = lower>>16; break; case 2: boardLine = upper&0xFFFF; break; case 3: boardLine = upper>>16; break; default: break; } return boardLine; } void Board::clearLine(int l){ //Clears the line indexed by board array int i; int f; for (i=l; i<(DIMZ+3); i++){ //Replace the line with the one above it if they are not the same. if (line[i] != line[i+1]){ line[i] = line[i+1]; //Then redraw the current line. for (f=0; f<4; f++){ redrawBoardLine(i-4, f, getFaceLine(f,i)); } } } line[DIMZ+3] = 0xF807F807F807F807ULL; for (f=0; f<4; f++){ redrawBoardLine(DIMZ-1, f, getFaceLine(f,DIMZ+3)); } score++; drawScore(); } void Board::redrawBoardLine(int l, int f, unsigned long long val){ //Redraws the board line using natural index. int offset = 0; for(offset = 0; offset<DIMX; offset++){ //Erase the pixel in the line. erasePix(l, offset, f); //check if we draw pixel here if ( (val&(0x01<<(offset+3))) > 0) drawPix(l,offset,f); } } void Board::drawScore(){ uLCD.locate(6,6); uLCD.text_width(1); //4X size text uLCD.text_height(1); uLCD.background_color(BLUE); uLCD.color(BLUE); uLCD.textbackground_color(BLACK); uLCD.set_font(FONT_7X8); uLCD.text_mode(OPAQUE); uLCD.printf("Score:"); uLCD.locate(6,8); uLCD.printf(" %d",score); } void Board::blockFall(){ nextBlock.height--; int h = nextBlock.height; while(check()){ h--; nextBlock.height--; } nextBlock.height = h+1; } void Board::checkLose(){ int i; for (i=DIMLOSE; i<DIMZ; i++){ if (line[i+4] != 0xF807F807F807F807ULL)lost=1; drawGrid(); } }