My ELEC2645 joystick project Tetris Game NAME: JIANWEI CHEN SID: 200879849

Dependencies:   N5110 SDFileSystem mbed

Game.h

Committer:
cjw851102
Date:
2016-05-05
Revision:
4:463abe5f5135

File content as of revision 4:463abe5f5135:

/**
@file Game.h
@brief Header file containing functions prototypes, defines and global variables for the tetris game.
@brief Revision 1.0.
@author JIANWEI CHEN
@date   May 2016
*/

#ifndef GAME_H
#define GAME_H

#include "mbed.h"
#include "Patterns.h"
#include "N5110.h"
#include "SDFileSystem.h"
#include "main.h"

//! Create patterns object
Patterns patterns;
/**
Create ticker object for game
@brief ticker to control the speed of game
*/
Ticker game;

////////////////////////////////////////////////////////
// Variables
////////////////////////////////////////////////////////

int xOld; /*!< Variable for storing the pattern previous position when game is runnung */
int typeCount = 0; /*!< Variable for counting elements in array typeArray[100]*/
int nextTypeCount; /*!< Variable to show the position of next element in typeArray[100] i.e. nextTypeCount=typeCount+1*/

/** Flag to show left hand side conllision of pattern
@note flag = 0 - there is no collision on the left
@note flag = 1 - there is collision on the left
*/
int left_collision_flag = 0;

/** Flag to show right hand side conllision of pattern
@note flag = 0 - there is no collision
@note flag = 1 - there is collision
*/
int right_collision_flag = 0;

/** Flag to show bottom conllision of pattern
@note flag = 0 - there is no collision
@note flag = 1 - there is collision
*/
int bottom_collision_flag = 0;

/** Flag to show top conllision of pattern
@note flag = 0 - there is no collision
@note flag = 1 - there is collision
*/
int top_collision_flag = 0;

/** Flag to show if there is pixels set on the 4 pixels distance away from the bottom of pattern
@brief if flag = 0, user can pull joystick down to make pattern falling faster
@note flag = 0 - there is no collision
@note flag = 1 - there is collision
*/
int fastmove_bottom_collision_flag = 0;

/** Flag to show if allow patterns to rotate
@note flag = 0 - allow
@note flag = 1 - not allow
*/
int rotation_collision_flag = 0;

/** Two dimension array to store current pattern pixels setting in a 6x6 square
@note For example, if current pattern is "L"
@note the pixels setting is
@note 1 1 0 0 0 0
@note 1 1 0 0 0 0
@note 1 1 0 0 0 0
@note 1 1 0 0 0 0
@note 1 1 1 1 0 0
@note 1 1 1 1 0 0
*/
int pattern_buffer[6][6];

/** Array to store types of pattern
@brief 100 random integers in the range of 0~6
@brief will be stored in the array in order to generate
@brief random type of pattern each time new pattern is generated
*/
int typeArray[100];
int buffer[84][48]; /*!< Array to store whole screen pixel settings */
int score=0; /*!< Variable to store score in the game */
volatile int g_game_flag = 0; /*!< Flag for game ticker */

/** Struct for position
@brief Contains the pattern position(x,y),
@brief pattern type and rotation
*/
struct Position {
    float x;
    float y;
    int type;
    int rotation;
};
typedef struct Position position;
position pos;

////////////////////////////////////////////////////////////////////////
// Functions
////////////////////////////////////////////////////////////////////////
/**
Game ticker isr function
*/
void game_isr();

/**
Initialise the variables in game
*/
void init_game();

/** Drawing the pattern at position (x,y)
@param type - type of pattern (0~6)
@param rotation - rotation of pattern (0~3)
@param x - x co-ordinate of pattern, which is the top left corner pixel of 6x6 square
@param y - y co-ordinate of pattern, which is the top left corner pixel of 6x6 square
@param fill - fill = 0 white, fill = 1 black
*/
void drawPattern(int type,int rotation,int x,int y,int fill);

/**Left collision detect
@brief Function to check the left collision of current falling pattern and set the left_collision_flag
*/
void left_collisionDetect();

/**Right collision detect
@brief Function to check the right collision of current falling pattern and set the left_collision_flag
*/
void right_collisionDetect();

/**Bottom collision detect
@brief Function to check the bottom collision of current falling pattern and set the right_collision_flag
*/
void bottom_collisionDetect();

/**top collision detect
@brief Function to check the top collision of current falling pattern and set the top_collision_flag
*/
void top_collisionDetect();

/**Rotation collision detect
@brief Function to check if the program should allow user to rotate the pattern and set the rotation_collision_flag
*/
void rotation_collisionDetect();

/**Fast move collision detect
@brief Function to check if the program should allow user to move the pattern falling faster and set the fastmove_collision_flag
@note check the 4 pixels distance away from the bottom of pattern
*/
void fastmove_bottom_collisionDetect();

/**Get pattern pixel settings
@brief Function to get the current falling pattern pixel settings
@note pixels setting will be store in pattern_buffer[6][6];
@param type - type of pattern (0~6)
@param rotation - rotation of pattern (0~3)
*/
void get_pattern(int type, int rotatin);

/**Scan the whole screen pixel settings
@brief Function to store the whole screen pixel settings and store in buffer[84][48]
*/
void scan();

/** Cancel the filled lines and add the score
@brief Function to check if there are lines filled
@brief if one line is filled, cancel it and move the pixels above this line down and add the score
*/
void cancelLine();

/**
Game finish animations
*/
void finishAnimation();

/** User press button when game is in process
@brief When button pressed in the game, ask user if they want to exit the game
@return TRUE - exit the game  FALSE - contiute the game
*/
bool buttonPressedInGame();

/**Save the highest score to SD card
@param score - a integer number need to save to SD card
*/
void save_score_SD(int score);

/**
Read the highest score from SD card
*/
int read_score_SD();

/**
Complete tetris game
*/
void tetis_game();


/////////////////////////////////////////////////
/// Function Definitions
/////////////////////////////////////////////////

void tetis_game()
{
    bool exitGame; //bool variable to save the exit game decision when button pressed
    while (1) {
        if(g_game_flag==1) {
            g_game_flag = 0;

            exitGame=buttonPressedInGame(); //check if user press button and get the decision
            if(exitGame) { // exit game
                break; //jump out of while loop
            }

            pos.type = typeArray[typeCount]; //get the pattern type
            nextTypeCount=typeCount+1;
            if (nextTypeCount>99) { //start from the first element in type array
                nextTypeCount=0;
            }
            drawPattern(typeArray[nextTypeCount],0,55,41,1);// draw next pattern on the right hand side of screen
            if (pos.y >= -5) { // when pattern start falling, clear previous pattern
                drawPattern(pos.type,pos.rotation,xOld,pos.y-1,0);
            }

            updateJoystick(); //get the joystick direction

            top_collisionDetect(); // check if current pattern touch the top
            if (top_collision_flag == 1) { // if touch the top, finish the game
                red_led=1;
                green_led=0;
                //play sound at end of game
                for(double i=1; i>=0; i-=0.1) {
                    buzzer = i;
                    wait(0.2);
                }
                buzzer = 0;
                finishAnimation(); //finish animation
                lcd.clear();
                int stored_score=read_score_SD();
                if (score>stored_score) { // if score is larger than highest score, save it to SD card
                    save_score_SD(score);
                }
                // show the score
                char scoreBuffer[14];
                sprintf(scoreBuffer,"%d",score);
                lcd.printString("SCORE IS: ",10,2);
                lcd.printString(scoreBuffer,35,4);
                lcd.refresh();
                wait(3.0);
                lcd.clear();
                state=0; // back to the main menu
                break;
            }

            //move patterns according to joystick direction
            switch(joystick.direction) {
                case UP: // rotation
                    rotation_collision_flag = 0;
                    rotation_collisionDetect(); // check if allow rotation
                    if(rotation_collision_flag == 0) { // allow rotation
                        pos.rotation++;
                        if (pos.rotation>3) {
                            pos.rotation=0;
                        }
                        drawPattern(pos.type,pos.rotation,pos.x,pos.y,1);
                    } else {// not allow rotation
                        drawPattern(pos.type,pos.rotation,pos.x,pos.y,1);
                    }
                    break;
                case DOWN: // faster moving down
                    fastmove_bottom_collision_flag = 0;
                    fastmove_bottom_collisionDetect();
                    if (fastmove_bottom_collision_flag == 0) {// allow faster move
                        pos.y +=4; // move pattern down by 4 pixels distance
                        drawPattern(pos.type,pos.rotation,pos.x,pos.y,1);
                    } else {
                        drawPattern(pos.type,pos.rotation,pos.x,pos.y,1);
                    }
                    break;
                case RIGHT: // move right
                    right_collision_flag = 0;
                    right_collisionDetect(); // detect right collision
                    if( right_collision_flag == 0) { // allow move right
                        pos.x +=2;
                        drawPattern(pos.type,pos.rotation,pos.x,pos.y,1);
                    } else { // not allow move right
                        drawPattern(pos.type,pos.rotation,pos.x,pos.y,1);
                        right_collision_flag = 0;
                    }
                    break;
                case LEFT: //move left
                    left_collision_flag = 0;
                    left_collisionDetect(); // detect left collision
                    if( left_collision_flag == 0) {// allow move left
                        pos.x -=2;
                        drawPattern(pos.type,pos.rotation,pos.x,pos.y,1);
                    } else { // not allow move left
                        drawPattern(pos.type,pos.rotation,pos.x,pos.y,1);
                        left_collision_flag = 0;
                    }

                    break;
                case CENTRE: // joystick in centre
                    drawPattern(pos.type,pos.rotation,pos.x,pos.y,1);
                    break;
                case  UNKNOWN:
                    drawPattern(pos.type,pos.rotation,pos.x,pos.y,1);
                    break;
            }

            xOld = pos.x; //store previous pattern x co-codinate

            bottom_collisionDetect(); // bottom collision detect
            if (bottom_collision_flag == 0) { // no collision
                pos.y++; // keep moving pattern down
                buzzer = 0; // turn off buzzer
            } else { // bottom collision
                drawPattern(pos.type,pos.rotation,pos.x,pos.y,1); // fix pattern
                cancelLine(); // check filled lines and add score
                pos.x = 10; // new pattern will fall from the centre
                pos.y = -6;
                pos.rotation=0;
                drawPattern(typeArray[nextTypeCount],pos.rotation,55,41,0);// clear the old next pattern show on RHS of screen
                typeCount ++; //new type of pattern
                if (typeCount >99) {
                    typeCount = 0;
                }
            }
            lcd.refresh();
        }
        sleep(); // go to sleep mode unless ticker interupt
    }
}


void init_game()
{
    pos.x = 10;
    pos.y = -6;
    pos.rotation=0;
    typeCount=0;
    green_led=1; // turn on green led
    //generate 100 random integers in the range of 0~6
    //and store in typeArray
    for(int i=0; i<=99; i++) {
        typeArray[i] = rand()%7;
    }

    //gaming screen
    lcd.drawLine(30,0,30,47,1);
    lcd.printString("Level:",42,0);
    lcd.printString("Score:",42,2);
    lcd.printString("0",42,3);
    lcd.printString("Next:",42,4);

    //play sound at begining of game
    for(double i=0; i<=1.0; i+=0.1) {
        buzzer = i;
        wait(0.2);
    }
    buzzer = 0;
}


void get_pattern(int type, int rotation)
{
    for(int i=0; i<=5; i++) {
        for(int j=0; j<=5; j++) {// patterns.getPatterns(type,rotation,j,i) return pattern[type][rotation][y][x];
            pattern_buffer[i][j] = patterns.getPatterns(type,rotation,j,i);
        }
    }
}


void scan()
{
    //screen has 84x48 pixels
    for (int i=0; i<=83; i++) {
        for (int j=0; j<=47; j++) {
            if(lcd.getPixel(i,j)) { //if pixel is set
                buffer[i][j] = 1;
            } else { // pixel is clear
                buffer[i][j] = 0;
            }
        }
    }
}

// draw pattern at (x,y)
void drawPattern(int type,int rotation,int x,int y,int fill)
{
    get_pattern(type,rotation); // get the pattern pixel settings, which store in pattern_buffer
    for(int i=x; i <= x+5; i++) { // (x,y) is the left top point of a 6*6 square
        for(int j=y; j <= y+5; j++) {
            if (j>=0) {
                if(pattern_buffer[i-x][j-y]==1) {//pixel setting is 1
                    if (fill==0) { //draw white pattern
                        if (j<=47 && i>=0) { // draw pattern inside the screen
                            lcd.clearPixel(i,j);
                        }
                    } else if (fill==1) { //draw black pattern
                        if (j<=47 && i>=0) {
                            lcd.setPixel(i,j);
                        }
                    }
                }
            }
        }
    }
}


void left_collisionDetect()
{
    int left_boundary[6][6];
    get_pattern(pos.type,pos.rotation);

    /* get the left boundary pixel settings
       e.g: if pattern is L
       left boundary is
       1 0 0 0 0 0
       1 0 0 0 0 0
       1 0 0 0 0 0
       1 0 0 0 0 0
       1 0 0 0 0 0
       1 0 0 0 0 0
    */
    for (int j=0; j<=5; j++) { //
        for (int i=0; i<=5; i++) {
            if (pattern_buffer[i][j]==1) {
                left_boundary[i][j]=1;
                for(int k=i+1; k<=5; k++) {
                    left_boundary[k][j] = 0;
                }
                break;
            } else {
                left_boundary[i][j]=0;
            }
        }
    }

    //check left collision
    int x = pos.x;
    int y = pos.y;
    if (x<0) { //for all the pattern, when x<0, pattern is on the most LHS of screen
        left_collision_flag = 1;
    } else { //check the pixel status away from left boundary for one pixel distance
        for(int i=x; i <= x+5; i++) { // (x,y) is the left top point of a 6*6 square
            for(int j=y; j <= y+5; j++) {
                if(left_boundary[i-x][j-y]==1) {
                    if(i == 0) { // pattern at most LHS of screen
                        left_collision_flag = 1;
                        break; // don't need to check the other pixels at same x, jump out of inner for loop
                    } else if (lcd.getPixel(i-1,j)) { //check one pixel away from left boundary
                        left_collision_flag = 1;
                        break;
                    } else {
                        left_collision_flag = 0;
                    }
                }
            }
            if (left_collision_flag == 1) {
                break;// jump out of inner for loop
            }
        }
    }
}


void right_collisionDetect()
{
    int right_boundary[6][6];
    get_pattern(pos.type,pos.rotation);

    /* get the right boundary pixel settings
       e.g: if pattern is L
       left boundary is
       0 0 1 0 0 0
       0 0 1 0 0 0
       0 0 1 0 0 0
       0 0 1 0 0 0
       0 0 0 1 0 0
       0 0 0 1 0 0
    */
    for (int j=0; j<=5; j++) {
        for (int i=5; i>=0; i--) {
            if (pattern_buffer[i][j]==1) {
                right_boundary[i][j]=1;
                for(int k=i-1; k>=0; k--) {
                    right_boundary[k][j] = 0;
                }
                break;
            } else {
                right_boundary[i][j]=0;
            }
        }
    }

    //check right collision
    int x = pos.x;
    int y = pos.y;
    for(int i = x; i <= x+5; i++) { // (x,y) is the left top point of a 6*6 square
        for(int j=y; j <= y+5; j++) {
            if(right_boundary[i-x][j-y]==1) {
                if(j>=0) {
                    if(i >= 29) { // most RHS of gaming screen
                        right_collision_flag = 1;
                        break;
                    } else if (lcd.getPixel(i+1,j)) {//check the pixel status away from right boundary for one pixel distance
                        right_collision_flag = 1;
                        break;
                    } else {
                        right_collision_flag = 0;
                    }
                }
            }
        }
        if (right_collision_flag == 1) {
            break;
        }
    }
}


void bottom_collisionDetect()
{
    int bot_boundary[6][6];
    get_pattern(pos.type,pos.rotation);

    /* get the left boundary pixel settings
       e.g: if pattern is L
       left boundary is
       0 0 0 0 0 0
       0 0 0 0 0 0
       0 0 0 0 0 0
       0 0 0 0 0 0
       0 0 0 0 0 0
       1 1 1 1 0 0
    */
    for (int i=0; i<=5; i++) {
        for (int j=5; j>=0; j--) {
            if (pattern_buffer[i][j]==1) {
                bot_boundary[i][j]=1;
                for(int k=j-1; k>=0; k--) {
                    bot_boundary[i][k] = 0;
                }
                break;
            } else {
                bot_boundary[i][j]=0;
            }
        }
    }

    //check bottom collision
    int x = pos.x;
    int y = pos.y;
    for(int i = x; i <= x+5; i++) { //check from left
        for(int j=y+5; j >= y; j--) { //check from bottom
            if (j>=-1) { //start check when pattern fall in the screen
                if(bot_boundary[i-x][j-y]==1) {
                    if(j >= 47) { // pattern at bottom
                        bottom_collision_flag = 1;
                        break; // jump out of inner for loop
                    } else if (lcd.getPixel(i,j+1)) {//check the pixel status away from bottom boundary for one pixel distance
                        bottom_collision_flag = 1;
                        break;
                    } else {
                        bottom_collision_flag = 0;
                    }
                }
            } else { //at bottom of screen
                bottom_collision_flag = 0;
            }
        }
        if( bottom_collision_flag == 1) {
            break; // jump out of inner for loop
        }
    }
}


void rotation_collisionDetect()
{
    int rotation = pos.rotation+1;
    if (rotation>3) {
        rotation=0;
    }
    get_pattern(pos.type,rotation);

    //check pixel status of upcoming rotation
    int x = pos.x;
    int y = pos.y;
    for(int i = x; i <= x+5; i++) { // (x,y) is the left top point of a 6*6 square
        for(int j=y; j <= y+5; j++) {
            if(pattern_buffer[i-x][j-y]==1) {
                if(i<0) { // out of LHS screen
                    rotation_collision_flag = 1;
                } else if(lcd.getPixel(i,j)) { // there is pixel set
                    rotation_collision_flag = 1;
                    break; //jump out of inner for loop
                } else {
                    rotation_collision_flag = 0;
                }
            }
        }
        if (rotation_collision_flag == 1) {
            break; //jump out of inner for loop
        }
    }
}


void top_collisionDetect()
{
    if (pos.y==-6) { //pattern is about to fall
        bottom_collisionDetect();
        if (bottom_collision_flag == 1) { //if can't fall
            top_collision_flag = 1; // top collision
        } else {
            top_collision_flag = 0;
        }
    }
}


void fastmove_bottom_collisionDetect()
{
    int bot_boundary[6][6];
    get_pattern(pos.type,pos.rotation);

    // get the bottom boundary pattern
    for (int i=0; i<=5; i++) {
        for (int j=5; j>=0; j--) {
            if (pattern_buffer[i][j]==1) {
                bot_boundary[i][j]=1;
                for(int k=j-1; k>=0; k--) {
                    bot_boundary[i][k] = 0;
                }
                break;
            } else {
                bot_boundary[i][j]=0;
            }
        }
    }

    //check bottom collision for 4 pixel distance away from bottom boundary
    int x = pos.x;
    int y = pos.y;
    for(int i = x; i <= x+5; i++) { // (x,y) is the left top point of a 6*6 square
        for(int j=y+5; j >= y; j--) {
            if (j>=-1) {
                if(bot_boundary[i-x][j-y]==1) {
                    if(j >= 42) { // pattern is about to fall on the bottom
                        fastmove_bottom_collision_flag = 1;
                        break;
                    } else if (lcd.getPixel(i,j+4)) {//check bottom collision for 4 pixel distance away from bottom boundary
                        fastmove_bottom_collision_flag = 1;
                        break;
                    } else {
                        fastmove_bottom_collision_flag = 0;
                    }
                }
            } else {
                fastmove_bottom_collision_flag = 0;
            }
        }
        if( fastmove_bottom_collision_flag == 1) {
            break;
        }
    }
}


void game_isr()
{
    g_game_flag = 1;
}


void cancelLine()
{
    // int linePattern[30][2]; //the pixel setting for one line(30x2 square)
    int count; // count setting pixels two by two
    for(int j=0; j<=46; j+=2) {
        for(int i=0; i<=29; i++) {
            if (lcd.getPixel(i,j)==0||lcd.getPixel(i,j+1)==0) { // there is clear pixel
                count=0;
                break;
            } else if (lcd.getPixel(i,j)&&lcd.getPixel(i,j+1)) {
                count++;
            }
        }
        if(count==30) { // one line is filled
            count=0; // reset the variable count
            lcd.drawRect(0,j,29,1,2); //clear the line
            buzzer = 0.5;
            score+=10;  // add the score
            //update the score
            char scoreBuffer[14];
            sprintf(scoreBuffer,"%d",score);
            lcd.printString(scoreBuffer,42,3);
            scan();
            // move the patterns above the line down for 2 pixels' hight
            for (int x=0; x<=29; x++) {
                for (int y=j; y>=0; y--) {
                    if (buffer[x][y]) {
                        lcd.clearPixel(x,y);
                        lcd.setPixel(x,y+2);
                    }
                }
            }
        }
    }
}


void finishAnimation()
{
    for (int j=47; j>=0; j--) {
        lcd.drawRect(0,j,29,1,1);
        wait(0.05);
        lcd.refresh();
    }
}


bool buttonPressedInGame()
{
    bool exit_game;
    if (g_button_flag) { // user press button finish game
        g_button_flag=0;
        scan(); // save the currnet gaming screen
        game.detach(); // detach the game ticker
        lcd.clear();
        while(1) {
            lcd.printString("Exit The Game?",1,1);
            lcd.printString("YES",38,3);
            lcd.printString("NO",38,4);
            pointer(); // invoke pointer function
            if (g_button_flag) {
                g_button_flag=0;
                if(pointer_position==0) { //"YES" -exit game
                    lcd.clear();
                    state=0; // back to main menu
                    pointer_position=0;
                    exit_game=true;
                    int stored_score=read_score_SD();
                    if (score>stored_score) { // if the score is higher than highest score, save it
                        save_score_SD(score);
                    }
                    score=0; //clear socre
                    red_led=1;
                    green_led=0;
                    break;
                } else { //"NO" - continue the game
                    exit_game=false;
                    if(state==3) { // game level is easy
                        lcd.clear();
                        for(int i=0; i<=83; i++) { // back to the gaming screen before press button
                            for(int j=0; j<=47; j++) {
                                if(buffer[i][j]) {
                                    lcd.setPixel(i,j);
                                }
                            }
                        }
                        game.attach(&game_isr,0.2); // easy game
                        exit_game=false;
                        break;
                    } else if(state==4) {// game level is hard
                        lcd.clear();
                        for(int i=0; i<=83; i++) {// back to the gaming screen before press button
                            for(int j=0; j<=47; j++) {
                                if(buffer[i][j]) {
                                    lcd.setPixel(i,j);
                                }
                            }
                        }
                        game.attach(&game_isr,0.1);// hard game
                        break;
                    }
                }
                break;
            }
        }
    }
    if(exit_game) {
        return true;
    } else {
        return false;
    }
}


int read_score_SD()
{
    fp = fopen("/sd/topscore.txt", "r"); //open file
    int stored_top_score;
    fscanf(fp, "%d",&stored_top_score); // ensure data type matches - note address operator (&)
    fclose(fp);  // ensure you close the file after reading
    return stored_top_score;
}


void save_score_SD(int score)
{
    fp = fopen("/sd/topscore.txt", "w");
    fprintf(fp, "%d",score); // ensure data type matches
    fclose(fp);  // ensure you close the file after writing
}
#endif