ELEC2645 Joseph Allison 200860349

Dependencies:   N5110 Tetris mbed

main.cpp

Committer:
el14ja
Date:
2016-05-05
Revision:
7:dfb290c7958c
Parent:
6:9cb8171ca6df

File content as of revision 7:dfb290c7958c:

/* ELEC2645 Project
week 19 - Created functions to work set pixels of the game area
        - Added the shapes and all their orientations
        - Created functions to place and remove shapes
        -added interupts and tickers for block moved and rotaion

week 20 - 7system function

week 21 - starting to implement collision detection

easter - colliosn detection
       - piece placement
       - scoring
       - levels and game speed changing
       - Game Complete
       
week 24 - All bugs removed
*/


#include "main.h"


int main()
{

    srand(floor(ain.read()*10000)); //Gets a random seed so the the random number generator creates a different set of random numbers each time the game is played
    
    lcd.init(); //initilises the screen

    startScreen();
    wait(2);

    setupInterruptInputs();

    newShapeBag();//create bag of shapes to use
    nextpiece = nextshape[1]; //initially sets the next piece
    tetris.gameInfo(0,1,nextpiece);
    
    musicticker.attach(&music_isr,0.125); //sets the speed that the music plays

    setTicker(gamespeed); //sets the speed that the block falls

    while (true) {

        gameMenu();

        if(musicplaying) { //checks to see if the music is playing
            buzzer.write(0.5);
        } else {
            buzzer.write(0);
        }

        score = 0; //sets all game varibles to default values
        level = 1;
        completedlines = 0;
        gamespeed = 0.8;
        
        setTicker(gamespeed);
        
        //resests the game screen
        setGameAreaArray();
        tetris.clearGame();
        tetris.gameSetup();
        tetris.gameInfo(score,level,nextpiece);


        while(gameplaying) { //loops throught the gane function whilst the game is running
            checkGameFlags();
            sleep();
        }

        gameOverScreen(); //when the game is over the game over screen is played

        //sets the game and game menu to run the next time ot come to them
        gameplaying = true;
        gamemenu = true;
    }
}

void gameMenu()
{

    int pointer = 0; //sets the pointer to its inital place

    lcd.clear();
    lcd.refresh();

    while(gamemenu) {

        //sets the menu text
        lcd.printString("PLAY",15,1);
        lcd.printString("MUSIC",15,3);
        lcd.printString("HELP",15,5);

        //checks the state fo the music to set the on/off label
        if(musicplaying) {
            lcd.printString("   ",50,3);
            lcd.printString("ON",50,3);
        } else {
            lcd.printString("OFF",50,3);
        }

        lcd.refresh();

        //checks the down button to move the pointer down
        if(g_down) {
            g_left = 0;
            g_right = 0;
            g_spin = 0;
            g_down = 0;
            if(pointer < 2) { //the max pos is 2 so the point doesnt move any lower
                pointer++;
            }

        }

        //checks the spin button to move the pointer up
        if(g_spin) {
            g_left = 0;
            g_right = 0;
            g_spin = 0;
            g_down = 0;
            if(pointer >0) { //0 is the lower pointer pos
                pointer--;
            }
        }

        //selects option 0 if the pointer is there and left is pressed 
        //selects main menu
        if(g_left && pointer == 0) {
            g_left = 0;
            g_right = 0;
            g_spin = 0;
            g_down = 0;
            gamemenu = false;
        }

        //selects option 1 if the pointer is there and left is pressed
        //turns music on
        if(g_left && pointer == 1) {
            g_left = 0;
            g_right = 0;
            g_spin = 0;
            g_down = 0;
            if(!musicplaying) {
                musicplaying = true;
            } else {
                musicplaying = false;
            }
            lcd.refresh();

        }

        //selects option 2 if the pointer is there and left is pressed
        //selects the help screen
        if(g_left && pointer == 2) {
            g_left = 0;
            g_right = 0;
            g_spin = 0;
            g_down = 0;
            helpScreen();
        }

        for(int i = 0; i<4; i++) { //clear the points before the new one is places
            for(int j = 10; j < 50; j++) {
                lcd.clearPixel(70+i,j);
            }
        }

        lcd.drawRect(70,10+ (pointer *16),3,3,0); //draws pointer on the screen
        lcd.refresh();

        sleep();
    }
    //set all the flags to zero before the game starts
}

void startScreen(){
    
    lcd.printString("TETRIS",27,1);
    lcd.printString("Created by",12,2);
    lcd.printString("Joe Allison",9,3);
    lcd.refresh();
    }


void gameOverScreen()
{
    buzzer.write(0);
    bool gameoverscreen = true;
    lcd.clear();

    lcd.printString("GAME OVER",15,1);

    //prints the score on the screen
    char buffer[14];
    int length = sprintf(buffer,"Score = %d",score);
    if(length <=14) {
        lcd.printString(buffer,0,3);
        lcd.refresh();
    }

    while (gameoverscreen) {
        //waits for any button to be pressed to leave the game over screen
        if(g_left || g_right || g_spin || g_down) {
            g_left = 0;
            g_right = 0;
            g_spin = 0;
            g_down = 0;
            gameoverscreen = false;
            lcd.clear();

        }
        sleep();
    }
}

void helpScreen()
{

    // help screen 1
    bool helpScreen = true;
    lcd.clear();
    //prints screen text
    lcd.printString("Move pieces",0,0);
    lcd.printString("with the D-pad",0,1);
    lcd.printString("to create rows",0,2);
    lcd.printString("Press any",0,4);
    lcd.printString("button",0,5);
    lcd.refresh();
    //waits for any button to be pressed
    while (helpScreen) {

        if(g_left || g_right || g_spin || g_down) {
            g_left = 0;
            g_right = 0;
            g_spin = 0;
            g_down = 0;
            helpScreen = false;
            lcd.clear();

        }
        sleep();
    }

    // help screen 2
    helpScreen = true;
    lcd.clear();

    //prints screen text and images
    lcd.drawCircle(42,5,4,0);
    lcd.drawCircle(31,16,4,0);
    lcd.drawCircle(53,16,4,0);
    lcd.drawCircle(42,26,4,0);
    lcd.printString("Spin",5,0);
    lcd.printString("Left",2,2);
    lcd.printString("Right",55,0);
    lcd.printString("Drop",60,3);
    lcd.drawLine(29,4,42,5,1); //spin
    lcd.drawLine(53,5,53,16,1); //right
    lcd.drawLine(25,19,31,16,1); //left
    lcd.drawLine(58,27,42,26,1); //drop

    lcd.printString("Press any",0,4);
    lcd.printString("button",0,5);
    lcd.refresh();

    //waits for any button to be pressed
    while (helpScreen) {

        if(g_left || g_right || g_spin || g_down) {
            g_left = 0;
            g_right = 0;
            g_spin = 0;
            g_down = 0;
            helpScreen = false;
            lcd.clear();
        }
        sleep();
    }


    // help screen 3

    helpScreen = true;
    lcd.clear();

    //prints screen text
    lcd.printString("Press left and",0,0);
    lcd.printString("right together",0,1);
    lcd.printString("to pause the",0,2);
    lcd.printString("game",0,3);


    lcd.printString("Press any",0,4);
    lcd.printString("button",0,5);
    lcd.refresh();

    //waits for any button to be pressed
    while (helpScreen) {

        if(g_left || g_right || g_spin || g_down) {
            g_left = 0;
            g_right = 0;
            g_spin = 0;
            g_down = 0;
            helpScreen = false;
            lcd.clear();
        }
        sleep();
    }
}

void checkGameFlags()
{
    if(g_pausemenu) { //if both left and right havent been pressed within 0.1s both flags are reset to 0
        g_pausemenu = 0;
        pauseright = 0;
        pauseleft = 0;
    }

    //checks if both left and right have been  pressed at the same time
    if(pauseright && pauseleft) {
        pauseright = 0;
        pauseleft = 0;
        lcd.clear();
        lcd.printString("PAUSE",27,2);
        lcd.refresh();
        buzzer.write(0.0); //silences music while on the pause menu
        while(1) { //waits in a loop til the both left and right are pressed

            if(pauseright && pauseleft) {
                pauseright = 0;
                pauseleft = 0;

                break;
            }
            sleep();
        }

        //sets all flags to 0 incase they are pressed during the pause screen
        g_left = 0;
        g_right = 0;
        g_spin = 0;
        g_down = 0;

        if(musicplaying) { //trubs music back on
            buzzer.write(0.5);
        } else {
            buzzer.write(0);
        }

        //resets the game area
        setGameArea();
        tetris.piecePlace(pieceposition[0],pieceposition[1],shapes[currentshape][orientation]);
    }

    //checks if a new piece need placing in the game
    if(g_newpiece) {
        g_newpiece = 0;
        newpiece();
    }

    //checks if the right button has been pressed
    if(g_right) {
        g_right = 0;
        movePieceRight();
    }

    //checks if the left button has been pressed
    if(g_left) {
        g_left = 0;
        movePieceLeft();
    }

    //checks the game time to see if the piece needs moving down
    if (g_timer_flag) {
        g_timer_flag = 0;

        //checks if there is a place for the piece to move down to, if not the piece is placed
        if(movePossible(pieceposition[0],pieceposition[1]+1,orientation)) { //returns false if piece cant move down

            movePieceDown();

        } else { //piece placed

            g_newpiece = 1; //a new piece is needed as the last one is placed
            pieceToGameArray(); //adds the placed piece to the game area

            if(harddropping) {//if piece has been placed after a hard drop the game speed is set back to normal
                harddropping = 0;
                setTicker(gamespeed);
            }

            //if a piece has been placed, it needs to be check to see if a full line is complete
            checkCompleteLine();
            setGameArea();

        }
    }

    if(g_spin) {
        g_spin = 0;
        spinPiece();

    }

    if(g_down) {
        g_down = 0;
        //when hard dropping the drop ticker is spead up
        setTicker(0.001);
        harddropping = 1;
    }


    if(g_music) {
        g_music = 0;
        buzzer.period_us(musisnotes[musiccounter]); //play the note in the musicnotes
        musiccounter++; //incriments the music counter

        if(musiccounter > 63) {
            musiccounter = 0;
        }

    }
}

void setupInterruptInputs(){
    //internal resistors set to pull down so that when the button is pressed the voltage is read as high
    left.mode(PullDown);
    right.mode(PullDown);
    spin.mode(PullDown);
    down.mode(PullDown);
    
    //sets the the interrupts to be called when the button is pressed down
    left.rise(&left_isr);
    right.rise(&right_isr);
    spin.rise(&spin_isr);
    down.rise(&down_isr);
    }

void setGameAreaArray()
{
    //fill gamearea add the walls
    //sets 1's around the bottoms and sides
    for(int i = 0; i<12; i++) {
        for(int j = 0; j<16; j++) {
            gamearea[i][j] = 1;
        }
    }
    //fills the middle with 0's
    for(int i = 1; i<11; i++) {
        for(int j = 0; j<15; j++) {
            gamearea[i][j] = 0;
        }
    }
}

bool movePossible(int xpos, int ypos, int orientation)
{

    if (orientation > 3) {
        orientation = 0;
    }
    int count = 0; //counter to count through all 16 bits

    for(int y = 0; y < 4; y++) { 
        for(int x = 0; x < 4; x++) {

            int bit = shapes[currentshape][orientation] & (1<<count); //find the bit of each pixel

            count ++;

            if ((bit !=0) && (gamearea[xpos+x+1][ypos+y]!=0)) { //compares each pixel of the piece witht he game array to check for collisions
                return false;
            }
        }
    }

    return true;
}

void newpiece() 
{

    orientation = 0; //set the orientation back to zero
    pieceposition[0] = 4; //sets the position back to the top
    pieceposition[1] = 0;

    //clearGame();
    //setGameArea();

    currentshape = nextshape[shapebagcounter]; //gets the next shape to place on the screen
    tetris.piecePlace(pieceposition[0],pieceposition[1],shapes[currentshape][orientation]);//places the new piece


    if(shapebagcounter > 5) { //when counter becomes 6
        newShapeBag(); //a new bag is created
        shapebagcounter = 0;
        nextpiece = nextshape[0];
    } else {
        nextpiece = nextshape[shapebagcounter+1];
        shapebagcounter++; //incrimenst the counter
    }
    
    tetris.gameInfo(score,level,nextpiece);

    if(!movePossible(pieceposition[0],pieceposition[1]+1,orientation)) {//if a piece is placed and imidiatly cant move the game is over
        gameplaying = false;
    }

}

void newShapeBag()  
{

    bool match = false;
    int newrandomnumber;

    nextshape[0] = rand()%7; //creates first random number

    for(int i = 1; i < 7; i++){ //loops to find all 7 numbers

        do {
            newrandomnumber = rand()%7;

            match = false; //set to false to end the do while if no match is found

            for(int j = 0; j < i; j++) {

                if (newrandomnumber == nextshape[j]) { //checks to see if number has already been chosen
                    match = true;// continue do while if a match is found
                    break; //end the for loop that is checking for a match
                }
            }
        } while(match);

        nextshape[i] = newrandomnumber; //set new found number into the newshape array
    }
}

void checkCompleteLine()   
{

    int rowscomplete[4] = {0,0,0,0}; //max of 4 rows can be completed with a single block
    int rowscompletecounter = 0; //counter to place complete row in the array
    int sum = 0;
    bool rowtoremove = false;
    for(int y = 0; y <15; y++) { //searches through all the rows

        for(int x = 1; x<11; x++) {
            if(gamearea[x][y] == 1) {
                sum++; //counts how many filled cells
            }
        }
        if(sum == 10) { //if all 10 cells are full
            //pc.printf("%d complete \n",y);
            rowscomplete[rowscompletecounter] = y; //sets it as a completed row
            rowscompletecounter++;
            rowtoremove = true;
        }
        sum = 0;
    }

    completedlines += rowscompletecounter; //adds the new completed rows to the row total
    if(rowtoremove) {//runs if a row is complete
        int prevlevel = level;
        level = int((completedlines/10)+1);

        if (prevlevel != level) {
            gamespeed = 0.8606*exp(-0.073*level); //the game speed is set depending on the level

            setTicker(gamespeed);
        }
        removeCompleteLines(rowscomplete); 
    }
    
    //increases the score depending on the number of rows complete
    if(rowscompletecounter == 1) {
        score += 100;
    } else if(rowscompletecounter == 2) {
        score += 250;
    } else if(rowscompletecounter == 3) {
        score += 500;
    } else if(rowscompletecounter == 4) {
        score += 1000;
    }
}

void removeCompleteLines(int rowscomplete[4])   
{
    for(int i = 0; i<4; i++) { //loops throught the max 4 lines that have been completed
        if(rowscomplete[i] >0) {

            for(int j = 1; j<11; j++) { //delete complete row
                gamearea[j][rowscomplete[i]] = 0;
            }

            for(int j = rowscomplete[i]; j>0; j--) { //runs through all the rows to copy, j starts at deleted row
                for(int k = 1; k<11; k++) { //runs through all the numbers in the row
                    gamearea[k][j] = gamearea[k][j-1];
                }
            }
            for(int j = 1; j<11; j++) { //add zeros into the top line
                gamearea[j][0] = 0;
            }
        }
    }
}

void pieceToGameArray()
{
    int count = 0; //looks through the 16 bits of the shape
    for(int y = 0; y<4; y++) {
        for(int x = 0; x<4; x++) {
            int bit = shapes[currentshape][orientation] & (1<<count); //finds the bits of each shape

            if (bit) {
                gamearea[pieceposition[0]+x+1][pieceposition[1]+y]=1; //sets the shape in the game area array
            }
            count++;
        }
    }
}

void setGameArea()
{
    tetris.clearGame();//clears the area
    for(int x = 1; x<11; x++) { //loops through the whole game array
        for(int y = 0; y<15; y++) {
            if(gamearea[x][y] == 1) {
                tetris.gamePixel(x-1,y); //x-1 because the gamearea array has 1's around the outside
            }
        }
    }
}

void setTicker(float time)
{
    ticker.detach();
    ticker.attach(&moveDownTicker, time);
}



void left_isr()
{
    if(g_lefttimeout) {
        g_left = 1;
        g_lefttimeout = 0;
        pauseleft = 1;
        lefttimeout.attach(&lefttimeout_isr,0.15); //sets the timeout so the button cant be pressed in quick sucsession
        pausemenu.attach(&pausemenu_isr,0.09); //sets timeout for the detection of both left and right pressed at the same time
    }
}
void right_isr()
{
    if(g_righttimeout) {
        g_right = 1;
        g_righttimeout = 0;
        pauseright = 1;
        righttimeout.attach(&righttimeout_isr,0.15);//sets the timeout so the button cant be pressed in quick sucsession
        pausemenu.attach(&pausemenu_isr,0.09); //sets timeout for the detection of both left and right pressed at the same time
    }
}
void spin_isr()
{
    if(g_spintimeout) {
        g_spin = 1;
        g_spintimeout = 0;
        spintimeout.attach(&spintimeout_isr,0.15);//sets the timeout so the button cant be pressed in quick sucsession
    }
}

void down_isr()
{
    if(g_downtimeout) {
        g_down = 1;
        g_downtimeout = 0;
        downtimeout.attach(&downtimeout_isr,0.4);//sets the timeout so the button cant be pressed in quick sucsession
    }
}

//ISR's and flag setting
void moveDownTicker()
{
    g_timer_flag = 1;
}

void music_isr()
{
    g_music = 1;
}

void lefttimeout_isr()
{
    g_lefttimeout = 1;
}
void righttimeout_isr()
{
    g_righttimeout = 1;
}
void spintimeout_isr()
{
    g_spintimeout = 1;
}
void downtimeout_isr()
{
    g_downtimeout = 1;
}

void pausemenu_isr()
{
    g_pausemenu = 1;
}


//each move position it checks if the piece can be moved there and then moves them
void movePieceRight()
{
    if(movePossible(pieceposition[0]-1,pieceposition[1],orientation)) {
        movePiece(1, 0,0);
    }
}
void movePieceLeft()
{
    if(movePossible(pieceposition[0]+1,pieceposition[1],orientation)) {
        movePiece(-1, 0,0);
    }
}
void spinPiece()
{
    if(movePossible(pieceposition[0],pieceposition[1],orientation+1)) {

        movePiece(0, 0,1);
    }
}

void movePieceDown()
{
    movePiece(0, 1,0);
}


void movePiece(int x, int y,int spin)
{
    tetris.pieceClear(pieceposition[0],pieceposition[1],shapes[currentshape][orientation]); //clears the piece before the piece is moved
    //changes the piece position or orientation
    pieceposition[0]= pieceposition[0] - x;
    pieceposition[1]= pieceposition[1] + y;
    orientation = orientation + spin;
    if (orientation > 3) {
        orientation = 0;
    }
    tetris.piecePlace(pieceposition[0],pieceposition[1],shapes[currentshape][orientation]); //resets the piece
}