My ELEC2645 joystick project Tetris Game NAME: JIANWEI CHEN SID: 200879849
Dependencies: N5110 SDFileSystem mbed
Revision 4:463abe5f5135, committed 2016-05-05
- Comitter:
- cjw851102
- Date:
- Thu May 05 09:18:01 2016 +0000
- Parent:
- 3:5494a0fb3a33
- Commit message:
- Final version
Changed in this revision
--- /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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Joystick.h Thu May 05 09:18:01 2016 +0000 @@ -0,0 +1,92 @@ +/** +@file Joystick.h +@brief Header file containing functions prototypes, defines and global variables for constructing joystick. +@brief Example code of how to read a joystick with slightly changed for the game +@https://www.sparkfun.com/products/9032 +@author Craig A. Evans +@date 7 March 2015 +*/ + +#ifndef JOYSTICK_H +#define JOYSTICK_H + +#define DIRECTION_TOLERANCE 0.25 + +/** +Joystick vertical direction +@brief Analog input for joystick to send vertical directions data +*/ +AnalogIn yPot(PTB2); +/** +Joystick horizontal direction +@brief Analog input for joystick to send horizontal direction data +*/ +AnalogIn xPot(PTB3); + +//! create enumerated type (0,1,2,3 etc. for direction) +//! could be extended for diagonals etc. +enum DirectionName { + UP, + DOWN, + LEFT, + RIGHT, + CENTRE, + UNKNOWN +}; +//! struct for Joystick +typedef struct JoyStick Joystick; +struct JoyStick { + double x; // current x value + double x0; // 'centred' x value + double y; // current y value + double y0; // 'centred' y value + int button; // button state (assume pull-down used, so 1 = pressed, 0 = unpressed) + DirectionName direction; // current direction +}; +//! create struct variable +Joystick joystick; +/** +Read default positions of the joystick to calibrate later readings +*/ +void calibrateJoystick(); +/** +Update the current position of joystick +*/ +void updateJoystick(); + +///////////////////////////////////////////////// +/// Function Definitions +///////////////////////////////////////////////// + +// read default positions of the joystick to calibrate later readings +void calibrateJoystick() +{ + // must not move during calibration + joystick.x0 = xPot; // initial positions in the range 0.0 to 1.0 (0.5 if centred exactly) + joystick.y0 = yPot; +} + +void updateJoystick() +{ + // read current joystick values relative to calibrated values (in range -0.5 to 0.5, 0.0 is centred) + joystick.x = xPot - joystick.x0; + joystick.y = yPot - joystick.y0; + + // calculate direction depending on x,y values + // tolerance allows a little lee-way in case joystick not exactly in the stated direction + if ( fabs(joystick.y) < DIRECTION_TOLERANCE && fabs(joystick.x) < DIRECTION_TOLERANCE) { + joystick.direction = CENTRE; + } else if ( joystick.y < DIRECTION_TOLERANCE && fabs(joystick.x) < DIRECTION_TOLERANCE) { + joystick.direction = UP; + } else if ( joystick.y > DIRECTION_TOLERANCE && fabs(joystick.x) < DIRECTION_TOLERANCE) { + joystick.direction = DOWN; + } else if ( joystick.x < DIRECTION_TOLERANCE && fabs(joystick.y) < DIRECTION_TOLERANCE) { + joystick.direction = RIGHT; + } else if ( joystick.x > DIRECTION_TOLERANCE && fabs(joystick.y) < DIRECTION_TOLERANCE) { + joystick.direction = LEFT; + } else { + joystick.direction = UNKNOWN; + } +} + +#endif \ No newline at end of file
--- a/Patterns.h Sun May 01 23:09:33 2016 +0000 +++ b/Patterns.h Thu May 05 09:18:01 2016 +0000 @@ -1,9 +1,27 @@ +/** +@file Patterns.h +@brief Header file containing member functions and variables +@brief Header file patterns pixel settings and funtion to get specific pattern pixel setting +@brief Revision 1.0. +@author JIANWEI CHEN +@date May 2016 +*/ + #ifndef PATTERNS_H #define PATTERNS_H + + class Patterns { public: - + /** Get pattern pixel setting + * + * @param type - pattern type (0~6) + * @param rotation - pattern rotation (0~3) + * @param y - pattern y co-ordinate (0~6) + * @param x - pattern x co-ordinate (0~6) + * @note (x,y) is the top left corner pixel of a 6x6 square + */ int getPatterns(int type, int rotation, int y, int x); private:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SDFileSystem.lib Thu May 05 09:18:01 2016 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/SDFileSystem/#7b35d1709458
--- a/main.cpp Sun May 01 23:09:33 2016 +0000 +++ b/main.cpp Thu May 05 09:18:01 2016 +0000 @@ -1,577 +1,71 @@ #include "mbed.h" #include "Patterns.h" #include "N5110.h" - -#define DIRECTION_TOLERANCE 0.05 - -//VCC, SCE, RST, D/C, MOSI,SCLK, LED -N5110 lcd(PTE26,PTA0,PTC4,PTD0,PTD2,PTD1,PTC3); -AnalogIn yPot(PTB2); -AnalogIn xPot(PTB3); -DigitalIn button(PTB11); -Patterns patterns; - -Ticker game; -void game_isr(); -volatile int g_game_flag = 0; - -void drawPattern(int type,int rotation,int x,int y,int fill); // draw pattern at (x,y), fill = 0 white, fill = 1 black - -void left_collisionDetect(); -int left_collision_flag = 0; - -void right_collisionDetect(); -int right_collision_flag = 0; - -void bottom_collisionDetect(); -int bottom_collision_flag = 0; - -void top_collisionDetect(); -int top_collision_flag = 0; - -void rotation_collisionDetect(); -int rotation_collision_flag = 0; - -void fastmove_bottom_collisionDetect(); -int fastmove_bottom_collision_flag = 0; - -struct Position { - float x; - float y; - int type; - int rotation; -}; -typedef struct Position position; - -position pos; - -void init_game(); -int typeArray[100]; -int rotationArray[100]; - -int pattern_buffer[6][6]; -void get_pattern(int type, int rotatin); - -void scan(); -int buffer[84][48]; - -// create enumerated type (0,1,2,3 etc. for direction) -// could be extended for diagonals etc. -enum DirectionName { - UP, - DOWN, - LEFT, - RIGHT, - CENTRE, - UNKNOWN -}; -// struct for Joystick -typedef struct JoyStick Joystick; -struct JoyStick { - double x; // current x value - double x0; // 'centred' x value - double y; // current y value - double y0; // 'centred' y value - int button; // button state (assume pull-down used, so 1 = pressed, 0 = unpressed) - DirectionName direction; // current direction -}; -// create struct variable -Joystick joystick; -void calibrateJoystick(); -void updateJoystick(); - -void cancelLine();// if one line is filled, cancel it and add the score -int score=0; - -void finishAnimation(); - -int xOld; -int typeCount = 0; -int nextTypeCount; +#include "SDFileSystem.h" +#include "Joystick.h" +#include "Game.h" +#include "main.h" int main() { - wait(2.0); // short delay for power to settle - lcd.init(); - lcd.normalMode(); // normal colour mode - lcd.setBrightness(1.0); // put LED backlight on 100% - init_game(); - game.attach(&game_isr,0.2); - lcd.refresh(); - calibrateJoystick(); - - while (1) { + init(); + while(1) { + if(g_menuTimer_flag) { + g_menuTimer_flag=0; - if(g_game_flag==1) { - g_game_flag = 0; - pos.type = typeArray[typeCount]; - nextTypeCount=typeCount+1; - if (nextTypeCount>99) { - nextTypeCount=0; - } - drawPattern(typeArray[nextTypeCount],0,55,41,1);// draw next pattern - if (pos.y >= -5) { // clear previous pattern - drawPattern(pos.type,pos.rotation,xOld,pos.y-1,0); - } - updateJoystick(); - top_collisionDetect(); - if (top_collision_flag == 1) { - finishAnimation(); + if (g_button_flag) {// if button pressed, go to next menu according current state and pointer position input + g_button_flag=0; + state=fsm[state].nextState[pointer_position]; lcd.clear(); - char scoreBuffer[14]; - sprintf(scoreBuffer,"%d",score); - lcd.printString("SCORE IS: ",10,2); - lcd.printString(scoreBuffer,35,3); - lcd.refresh(); - break; - } - switch(joystick.direction) { - case UP: - rotation_collisionDetect(); - if(rotation_collision_flag == 0) { - pos.rotation++; - if (pos.rotation>3) { - pos.rotation=0; - } - drawPattern(pos.type,pos.rotation,pos.x,pos.y,1); - } else {// if collision - drawPattern(pos.type,pos.rotation,pos.x,pos.y,1); - } - break; - case DOWN: - fastmove_bottom_collisionDetect(); - if (fastmove_bottom_collision_flag == 0) { - pos.y +=4; - drawPattern(pos.type,pos.rotation,pos.x,pos.y,1); - } else { - drawPattern(pos.type,pos.rotation,pos.x,pos.y,1); - } - break; - case RIGHT: - right_collisionDetect(); - if( right_collision_flag == 0) { - pos.x +=2; - drawPattern(pos.type,pos.rotation,pos.x,pos.y,1); - } else { - drawPattern(pos.type,pos.rotation,pos.x,pos.y,1); - right_collision_flag = 0; - } - break; - case LEFT: - left_collisionDetect(); - if( left_collision_flag == 0) { - pos.x -=2; - drawPattern(pos.type,pos.rotation,pos.x,pos.y,1); - } else { - drawPattern(pos.type,pos.rotation,pos.x,pos.y,1); - left_collision_flag = 0; - } - - break; - case 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; - bottom_collisionDetect(); - if (bottom_collision_flag == 0) { - pos.y++; - } else { - drawPattern(pos.type,pos.rotation,pos.x,pos.y,1);// fix pattern - cancelLine(); - pos.x = 10; - pos.y = -6; - pos.rotation=0; - drawPattern(typeArray[nextTypeCount],pos.rotation,55,41,0);// clear the old next pattern - typeCount ++; - if (typeCount >99) { - typeCount = 0; - } } - } - lcd.refresh(); - } - -} - -void init_game() -{ - pos.x = 10; - pos.y = -6; - pos.rotation=0; - for(int i=0; i<=99; i++) { - typeArray[i] = rand()%7; - rotationArray[i] = rand()%5; - } - lcd.drawLine(30,0,30,47,1); - lcd.printString("Level:",42,0); - lcd.printString("easy",42,1); - lcd.printString("Score:",42,2); - lcd.printString("0",42,3); - lcd.printString("Next:",42,4); - -} - -void get_pattern(int type, int rotation) -{ - for(int i=0; i<=5; i++) { - for(int j=0; j<=5; j++) { - pattern_buffer[i][j] = patterns.getPatterns(type,rotation,j,i); // return pattern[type][rotation][y][x]; - } - } -} - -void scan() -{ - for (int i=0; i<=83; i++) { - for (int j=0; j<=47; j++) { - if(lcd.getPixel(i,j)) { - buffer[i][j] = 1; - } else { - buffer[i][j] = 0; - } - } - } -} - - -void drawPattern(int type,int rotation,int x,int y,int fill) -{ - get_pattern(type,rotation); - 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) { - if (fill==0) { - if (j<=47 && i>=0) { - lcd.clearPixel(i,j); - } - } else if (fill==1) { - 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 pattern - 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) { - left_collision_flag = 1; - } else { - 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) { - left_collision_flag = 1; - break; - } else if (lcd.getPixel(i-1,j)) { - left_collision_flag = 1; - break; - } else { - left_collision_flag = 0; - } - } - } - if (left_collision_flag == 1) { - break; - } - } - } -} - - -void right_collisionDetect() -{ - int right_boundary[6][6]; - get_pattern(pos.type,pos.rotation); - // get the left boundary pattern - 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; + switch(state) { + case 0: // main menu + pointer(); + red_led=0; + green_led=0; + lcd.printString("Tetis Game",10,1); + lcd.printString("START",25,3); + lcd.printString("SCORE",25,4); + lcd.refresh(); + break; + case 1: // game level choosing menu + pointer(); + red_led=0; + green_led=0; + lcd.printString("Game Level",10,1); + lcd.printString("EASY",25,3); + lcd.printString("HARD",25,4); + lcd.printString("Back",60,5); + lcd.refresh(); + break; + case 2: // highest score menu + red_led=0; + green_led=0; + lcd.printString("Highest Score:",1,1); + int top_score; + top_score=read_score_SD(); + char score[14]; + sprintf(score,"%d",top_score); + lcd.printString(score,30,3); + lcd.printString("Press Button",0,4); + lcd.printString("To Exit",0,5); + lcd.refresh(); + break; + case 3: // easy game menu + init_game(); + game.attach(&game_isr,0.2); + lcd.printString("easy",42,1); + tetis_game(); + break; + case 4: // hard game menu + init_game(); + game.attach(&game_isr,0.1); + lcd.printString("hard",42,1); + tetis_game(); + break; } } - } - - //check left 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) { - right_collision_flag = 1; - break; - } else if (lcd.getPixel(i+1,j)) { - 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 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 left 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+5; j >= y; j--) { - if (j>=-1) { - if(bot_boundary[i-x][j-y]==1) { - if(j >= 47) { - bottom_collision_flag = 1; - break; - } else if (lcd.getPixel(i,j+1)) { - bottom_collision_flag = 1; - break; - } else { - bottom_collision_flag = 0; - } - } - } else { - bottom_collision_flag = 0; - } - } - if( bottom_collision_flag == 1) { - break; - } - } -} - -void rotation_collisionDetect() -{ - int rotation = pos.rotation+1; - if (rotation>3) { - rotation=0; - } - get_pattern(pos.type,rotation); - - //check - 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) { - rotation_collision_flag = 1; - } else if(lcd.getPixel(i,j)) { - rotation_collision_flag = 1; - break; - } else { - rotation_collision_flag = 0; - } - } - } - if (rotation_collision_flag == 1) { - break; - } - } -} - -void top_collisionDetect() -{ - if (pos.y==-6) { - bottom_collisionDetect(); - if (bottom_collision_flag == 1) { - top_collision_flag = 1; - } else { - top_collision_flag = 0; - } + sleep(); } } - -void fastmove_bottom_collisionDetect() -{ - int bot_boundary[6][6]; - get_pattern(pos.type,pos.rotation); - // get the left 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 left 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+5; j >= y; j--) { - if (j>=-1) { - if(bot_boundary[i-x][j-y]==1) { - if(j >= 42) { - fastmove_bottom_collision_flag = 1; - break; - } else if (lcd.getPixel(i,j+4)) { - 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; -} - -// read default positions of the joystick to calibrate later readings -void calibrateJoystick() -{ - button.mode(PullDown); - // must not move during calibration - joystick.x0 = xPot; // initial positions in the range 0.0 to 1.0 (0.5 if centred exactly) - joystick.y0 = yPot; -} - -void updateJoystick() -{ - // read current joystick values relative to calibrated values (in range -0.5 to 0.5, 0.0 is centred) - joystick.x = xPot - joystick.x0; - joystick.y = yPot - joystick.y0; - // read button state - joystick.button = button; - - // calculate direction depending on x,y values - // tolerance allows a little lee-way in case joystick not exactly in the stated direction - if ( fabs(joystick.y) < DIRECTION_TOLERANCE && fabs(joystick.x) < DIRECTION_TOLERANCE) { - joystick.direction = CENTRE; - } else if ( joystick.y < DIRECTION_TOLERANCE && fabs(joystick.x) < DIRECTION_TOLERANCE) { - joystick.direction = UP; - } else if ( joystick.y > DIRECTION_TOLERANCE && fabs(joystick.x) < DIRECTION_TOLERANCE) { - joystick.direction = DOWN; - } else if ( joystick.x < DIRECTION_TOLERANCE && fabs(joystick.y) < DIRECTION_TOLERANCE) { - joystick.direction = RIGHT; - } else if ( joystick.x > DIRECTION_TOLERANCE && fabs(joystick.y) < DIRECTION_TOLERANCE) { - joystick.direction = LEFT; - } else { - joystick.direction = UNKNOWN; - } - -} - -void cancelLine() -{ - // int linePattern[30][2]; //the pixel setting for one line(30x2 square) - int count; - 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) { - count=0; - break; - } else if (lcd.getPixel(i,j)&&lcd.getPixel(i,j+1)) { - count++; - } - } - if(count==30) { // one line is filled - count=0; - lcd.drawRect(0,j,29,1,2); //clear the line - score+=10; // add the score - //print the score - char scoreBuffer[14]; - sprintf(scoreBuffer,"%d",score); - lcd.printString(scoreBuffer,42,3); - scan(); - // move the patterns upon 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(); - } -} - - -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.h Thu May 05 09:18:01 2016 +0000 @@ -0,0 +1,186 @@ +/** +@file main.h +@brief Header file containing functions prototypes, defines and global variables for menu and pointer +@brief Menu is construct using finit state mechine. +@brief The input for finit state mechine is the positer position. +@brief Revision 1.0. +@author JIANWEI CHEN +@date May 2016 +*/ +#ifndef MAIN_H +#define MAIN_H + +#include "mbed.h" +// VCC, SCE, RST, D/C, MOSI,SCLK, LED +N5110 lcd(PTE26,PTA0,PTC4,PTD0,PTD2,PTD1,PTC3); +// MOSI, MISO, SCK, CS +SDFileSystem sd(PTE3, PTE1, PTE2, PTE4, "sd"); + +FILE *fp; /*!< File pointer */ + +DigitalOut red_led(PTA1); +DigitalOut green_led(PTC2); +InterruptIn button(PTB18); +PwmOut buzzer (PTA2); + +//////////////////////////////////////////////////////// +// Variables +//////////////////////////////////////////////////////// +/** +Create ticker object for menu +@brief ticker to control the speed of updating the menu +*/ +Ticker menuTimer; + +volatile int g_menuTimer_flag = 0; /*!< Flag for menu ticker */ +volatile int g_button_flag = 0; /*!< Flag for button interrupt */ + +/**State of menu +@brief Variable control the state of menu +@note state 0 - main menu +@note state 1 - game level choosing menu +@note state 2 - show highest score menu +@note state 3 - game with easy level +@note state 4 - game with hard level +*/ +int state=0; + +int pointer_position=0;/*!< Variable control the position of pointer */ + +/**struct for state +*/ +struct State { + int nextState[3]; // array of next states +}; +typedef const struct State STyp; + +STyp fsm[5] = { + {1,2,0}, // states chosen according to inputs(pointer position) + {3,4,0}, + {0,0,0}, + {0,0,0}, + {0,0,0} +}; + +////////////////////////////////////////////////////////////////// +// Functions +////////////////////////////////////////////////////////////////// + +/** +Menu isr function +*/ +void menuTimer_isr(); + +/** +Initialise the program and lcd, mbed initial settings +*/ +void init(); + +/** +Button interrupt isr function +*/ +void button_isr(); + +/**Drawing the pointer at (x,y) +@brief Drawing the pointer image at (x,y) according to inputs +@param x - x co-ordinate of lcd +@param y - y co-ordinate of lcd +@param fill - fill = 0 white, fill = 1 black +*/ +void pointer_image(int x,int y,int fill);//0-white 1-black + +/** Move the pointer +@brief Function to control the pointer according to joystick +*/ +void pointer(); + + +///////////////////////////////////////////////// +/// Function Definitions +///////////////////////////////////////////////// +void menuTimer_isr() +{ + g_menuTimer_flag = 1; +} + + +void button_isr() +{ + g_button_flag=1; +} + + +void pointer() +{ + updateJoystick(); // get joystick direction + if (joystick.direction==UP) { + if(pointer_position==0) { // pointer at the top + pointer_position=0; + } else { + pointer_position--; + } + } else if(joystick.direction==DOWN) { + if(pointer_position==1&&(state!=1)) { //only game level choosing menu have 3 pointer positions + pointer_position=1; + } else if (pointer_position==2&&(state==1)) { + pointer_position=2; + } else { + pointer_position++; + } + } + + if(pointer_position==0) { + pointer_image(10,27,1); // draw pointer + pointer_image(10,35,0); // clear pointer at other positions + pointer_image(10,43,0); + } else if(pointer_position==1) { + pointer_image(10,27,0); + pointer_image(10,35,1); + pointer_image(10,43,0); + } else if(pointer_position==2) { + pointer_image(10,35,0); + pointer_image(10,43,1); + } + +} + + +void pointer_image(int x,int y,int fill) +{ + if(fill==0) {// draw white pointer + lcd.drawLine(x,y,x-4,y+2,0); + lcd.drawLine(x,y,x-4,y-2,0); + } else { // draw black pointer + lcd.drawLine(x,y,x-4,y-2,1); + lcd.drawLine(x,y,x-4,y+2,1); + } +} + + +void init(){ + wait(2.0); // short delay for power to settle + // initialise sd card + fp = fopen("/sd/topscore.txt", "r"); + fclose(fp); // ensure you close the file after reading + lcd.init(); // initialise lcd + lcd.normalMode(); // normal colour mode + lcd.setBrightness(1.0); // put LED backlight on 100% + + lcd.printString("Calibrating",0,0); + lcd.printString("Joystick........",0,1); + lcd.printString("PLEASE DO NOT",0,3); + lcd.printString("MOVE JOYSTICK.....",0,4); + lcd.refresh(); + calibrateJoystick(); + wait(4.0); + + lcd.clear(); + lcd.refresh(); + button.mode(PullDown); + red_led=0; + green_led=0; + button.fall(&button_isr); + menuTimer.attach(&menuTimer_isr,0.1); // attach menu ticker + buzzer.period(1.0/4000.0); // set buzzer period to 4 kKz +} +#endif \ No newline at end of file
--- a/mbed.bld Sun May 01 23:09:33 2016 +0000 +++ b/mbed.bld Thu May 05 09:18:01 2016 +0000 @@ -1,1 +1,1 @@ -http://mbed.org/users/mbed_official/code/mbed/builds/87f2f5183dfb \ No newline at end of file +http://mbed.org/users/mbed_official/code/mbed/builds/aae6fcc7d9bb \ No newline at end of file