Snake Project ELEC2645 John A. C. Richards 200869600

Dependencies:   N5110 SDFileSystem mbed

main.cpp

Committer:
JAC__RICHARDS
Date:
2016-05-05
Revision:
0:3ea41570bc9f

File content as of revision 0:3ea41570bc9f:

//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;
}