Snake Project ELEC2645 John A. C. Richards 200869600
Dependencies: N5110 SDFileSystem mbed
Diff: main.cpp
- Revision:
- 0:3ea41570bc9f
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Thu May 05 09:09:59 2016 +0000 @@ -0,0 +1,421 @@ +//ELEC2645 project +//John A. C. Richards, 200869600 + + +#include "main.h" + +//Timer for LED to be on +Timeout greenTimeout; + +bool nextFrameFlag = false; //initial setting for flag to change to next frame +bool changeDirection = false; //initial setting for flag to change direction +bool timerDone; //bool for timeout + +// timer to regularly read the joystick +Ticker pollJoystick; +// Serial for debug +Serial serial(USBTX,USBRX); + +// 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 { + float x; // current x value + float x0; // 'centred' x value + float y; // current y value + float 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; + +int printFlag = 0; + +// function prototypes +void calibrateJoystick(); +void updateJoystick(); + +//Start main program +int main() +{ + snake(); + +} + +void snake() +{ + calibrateJoystick(); // get centred values of joystick + pollJoystick.attach(&updateJoystick,1.0/10.0); // read joystick 10 times per second + + serial.baud(115200); // full-speed! + serial.printf("High-Score\n"); + FILE *fp; // this is our file pointer + wait(1); + + //set score and high score to 0 + int score = 0; + int highScore = 0; + + //set high score as SD card value if one + fp = fopen("/sd/highscore.txt", "r"); + int stored_high_score = -1; // -1 to demonstrate it has changed after reading + + if (fp == NULL) { // if it can't open the file then print error message + serial.printf("Error! Unable to open file!\n"); + highScore = 0; + } else { // opened file so can write + fscanf(fp, "%d",&stored_high_score); // ensure data type matches - note address operator (&) + serial.printf("Read %d from file.\n",stored_high_score); + fclose(fp); // ensure you close the file after reading + highScore = stored_high_score; + } + + //start with mbed LEDs off + green_led = 1; + red_led = 1; + + //start up blank screen + lcd.init(); + lcd.clear(); + lcd.setBrightness(1.0); + + //display splash screen + lcd.printString("SNAKE",27,1); + lcd.printString("By John A. C.",1,3); + lcd.printString("Richards (JAC)",1,4); + lcd.printString("200869600",1,5); + + //wait 5 seconds before moving on - using timeout + screenDelay (5.0); + + //set tickers for refreshing the screen and changing direction + Ticker refreshTicker; + Ticker directionTicker; + + //while program is running + while (1) { + score = 0; + bool gameOver = false; //start with game playing + + green_led = 0; //mbed LED green for game playing + red_led = 1; + + //set initial snake parameters + int playerX = 20; //snake start position x plane + int playerY = 10; //snake start position y plane + playerLength = 5; //snake start length + int playerSize = 2; //snake width + + //For moving, add one to start and take one from end + for (int i=0; i<maxPlayerSize; i++) { + playerPositionsX[i] = -1; + playerPositionsY[i] = -1; + } + + //Player direction - start by moving right + int directionX = 1; + int directionY = 0; + + //set initial fruit location + int random = std::rand(); + int fruitPositionX = random % areaWidth; + random = std::rand(); + int fruitPositionY = random % areaHeight; + + //game refresh rate - set with ticker + refreshTicker.attach(&nextFrame, 0.15); + + //while player is alive + while (!gameOver) { + + //sleep to conserve resources until reached again + sleep(); + + //set flag for refreshing the screen + if (nextFrameFlag) { + nextFrameFlag = false; + + + // check joystick direction + switch (joystick.direction) { + case UP: //up + if (!(directionY == -1)) { + directionX = 0; + directionY = 1; + } + break; + case DOWN: //down + if (!(directionY == 1)) { + directionX = 0; + directionY = -1; + } + break; + case LEFT: //left + if (!(directionX == 1)) { + directionX = -1; + directionY = 0; + } + break; + case RIGHT: //right + if (!(directionX == -1)) { + directionX = 1; + directionY = 0; + } + break; + + } + + //Move the player + playerX += directionX; //add direction x to snake head to move in x plane + playerY += directionY; //add direction y to snake head to move in y plane + + + + //Grow snake to starting length + for (int i=playerLength-1; i>=0; i--) { + playerPositionsX[i+1] = playerPositionsX[i]; + playerPositionsY[i+1] = playerPositionsY[i]; + } + playerPositionsX[0] = playerX; //set head as start of snake - x plane + playerPositionsY[0] = playerY; //set head as start of snake - y plane + + //check if player hit something + if(checkCollision(playerX,playerY)) + gameOver = true; //end game + + //check if player is about to hit something + if (checkCollision(playerX+directionX,playerY+directionY)) { //check if obstacle within one space + yellowLED = 1; //set yellow LED on if approaching obstacle + } else if + (checkCollision(playerX+directionX+directionX,playerY+directionY+directionY)) {//check if obstacle within two spaces + yellowLED = 1; //set yellow LED on if approaching obstacle + } else { + yellowLED = 0; //set yellow LED off if not approaching obstacle + + } + //If the player is in the same place as the fruit, then move it to another random position, increase the snake length, and increment score. + if (playerX == fruitPositionX + && playerY == fruitPositionY) { + + //increment score and update high score if necessary + score++; + if (score > highScore) { + highScore = score; + + //write new high score to SD card + fp = fopen("/sd/highScore.txt", "w"); + + if (fp == NULL) { // if it can't open the file then print error message + serial.printf("Error! Unable to open file!\n"); + } else { // opened file so can write + serial.printf("Writing to file...."); + fprintf(fp, "%d",score); // ensure data type matches + serial.printf("Done.\n"); + fclose(fp); // ensure you close the file after writing + } + } + + //make snake longer + playerLength++; + + //Turn on green LED + greenLED = 1; + greenTimeout.attach(greenOff,1.0); //turn off green LED after one second + + //Move fruit to random location + random = std::rand(); + fruitPositionX = random % areaWidth; //random x location + random = std::rand(); + fruitPositionY = random % areaHeight; //random y location + + //Make sure space for fruit is in empty space + for (int i=1; i<playerLength; i++) { + if (fruitPositionX == playerPositionsX[i] + && fruitPositionY == playerPositionsY[i]) { //check if fruit is in same position as any bit of snake + random = std::rand(); + fruitPositionX = random % areaWidth; //random x location + random = std::rand(); + fruitPositionY = random % areaHeight; //random y location + } + } + } + + //start of rendering code + lcd.clear(); //clear the screen + + //display score + std::string scoreTag = "SCR:"; //string saying SCR for score + char scoreStr[3]; //set 3 spaces for score value + sprintf(scoreStr, "%d", score); //string with score value + std::string result = scoreTag + scoreStr; // add two strings together + + lcd.printString(result.c_str(),0,0); //display score string + + //display high score + std:: string highScoreTag = "HI:"; //string saying HI for high-score + char highScoreStr[3]; //set 3 spaces for high-score value + sprintf(highScoreStr, "%d", highScore); //string with high-score value + std::string highResult = highScoreTag + highScoreStr; // add two strings together + + lcd.printString(highResult.c_str(),42,0); //display high-score string + + //Draw the walls + lcd.drawRect(0,8,83,39,0); //left side, right side, top, bottom, no fill + + //Draw player + for (int i=0; i<playerLength; i++) { + int actualPlayerX = playerPositionsX[i] * playerSize + 1; + int actualPlayerY = playerPositionsY[i] * playerSize + 9; + lcd.drawRect(actualPlayerX, actualPlayerY, playerSize-1, playerSize-1, 1); //left side, right side, top, bottom, filled + } + + //Draw the fruit + int actualFruitX = fruitPositionX * playerSize + 1; + int actualFruitY = fruitPositionY * playerSize + 9; + lcd.drawRect(actualFruitX, actualFruitY, playerSize-1, playerSize-1, 1); //left side, right side, top, bottom, filled + + lcd.refresh(); //refresh the screen + } + + } + //disable tickers + refreshTicker.detach(); + directionTicker.detach(); + + //clear screen for game over screen + lcd.clear(); + + //print game over texts + lcd.printString("GAME OVER",15,1); //string saying game over + + std::string scoreTag = "SCORE:"; + char scoreStr[3]; + sprintf(scoreStr, "%d", score); + std::string scoreResult = scoreTag + scoreStr; + lcd.printString(scoreResult.c_str(),1,3); //string saying score and value + + std::string highScoreTag = "HIGH-SCORE:"; + char highScoreStr[3]; + sprintf(highScoreStr, "%d", highScore); + std::string highScoreResult = highScoreTag + highScoreStr; + lcd.printString(highScoreResult.c_str(),1,4); //string saying high-score and value + + //set mbed LEDs + green_led = 1; + red_led = 0; //red LED on for game over + + //refresh to display texts + lcd.refresh(); + + //make sure game LEDs is off + yellowLED = 0; + greenLED = 0; + + //set buzzer volume to potentiometer value + buzzer = volume; + + //Play little tune on buzzer + buzzer.period(1.0/500.0); + screenDelay(0.5); + buzzer = volume; //check if volume has changed + buzzer.period(1.0/400.0); + screenDelay(0.5); + buzzer = volume;//check if volume has changed + buzzer.period(1.0/300.0); + screenDelay(1.0); + buzzer = 0; + screenDelay(1.5); + +} +} + +// 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; + } + // set flag for printing + printFlag = 1; +} + +void timerTrigger() //function to set bool as true to be used again +{ + timerDone = true; +} + +void screenDelay(float seconds) // timeout function +{ + + Timeout timer; + timerDone = false; + timer.attach(&timerTrigger, seconds); + while (!timerDone) + sleep(); +} + +void greenOff () //turn off LED after use +{ + greenLED = 0; +} + +bool checkCollision(int x,int y) //bool for collision checking +{ + //check if hit walls + if (x < 0 //x in same space as left wall + || x == areaWidth //or in same space as right wall + || y < 0 //or in same space as bottom wall + || y == areaHeight) { // or in same space as top wall + return true; //crashed + } +//check if hit self + for (int i=1; i<playerLength; i++) { + if (x == playerPositionsX[i] // if x head position is on snake body in x plane + && y == playerPositionsY[i]) { //and y head position is on snake body in y plane + return true; //crashed + } + + } + return false; //else not crashed +} + +//Set flag for next frame +void nextFrame() +{ + nextFrameFlag = true; +} \ No newline at end of file