My ELEC2645 joystick project Tetris Game NAME: JIANWEI CHEN SID: 200879849
Dependencies: N5110 SDFileSystem mbed
Diff: Game.h
- Revision:
- 4:463abe5f5135
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Game.h Thu May 05 09:18:01 2016 +0000 @@ -0,0 +1,816 @@ +/** +@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 \ No newline at end of file