Completed Snake Program

Dependencies:   N5110 PinDetect PowerControl mbed

Fork of DocTest by Craig Evans

main.cpp

Committer:
MBirney
Date:
2015-05-01
Revision:
9:77b83754460c
Parent:
8:b857684a3983
Child:
10:dbcb96fa049d

File content as of revision 9:77b83754460c:

#include "main.h"

// create enumerated type (0,1,2,3 etc. for direction)
enum DirectionName {
    UP,
    DOWN,
    LEFT,
    RIGHT,
    CENTRE,
    UNKNOWN
};

// create enumerated type (0,1,2 for difficulty)
enum Difficulty {
    EASY,
    MEDIUM,
    HARD,
};

// create enumerated type (0,1 for Game Mode)
enum GameMode {
    CLASSIC,
    BOUNDARY,
};

Difficulty currentDifficulty=EASY;// initialise to easy mode
GameMode   selectedGameMode=CLASSIC; //initialise to Classic Mode

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

DirectionName previousDirection =RIGHT;//initial direction =Right

// 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 = DOWN;
    } else if ( joystick.y < DIRECTION_TOLERANCE && fabs(joystick.x) < DIRECTION_TOLERANCE) {
        joystick.direction = UP;
    } else if ( joystick.x > DIRECTION_TOLERANCE && fabs(joystick.y) < DIRECTION_TOLERANCE) {
        joystick.direction = RIGHT; // remember switched this with right
    } else if ( joystick.x < DIRECTION_TOLERANCE && fabs(joystick.y) < DIRECTION_TOLERANCE) {
        joystick.direction = LEFT;
    } else {
        joystick.direction = UNKNOWN;
    }
}

//GAME FUNCTIONS

//For start MENU

void displaySplash()
{

    lcd.inverseMode();      // change colour mode
    lcd.setBrightness(0.5); // put LED backlight on 50%

    //Draw S
    lcd.drawRect(28,10,2,5,1);
    lcd.drawRect(15,10,15,2,1);
    lcd.drawRect(15,10,2,10,1);
    lcd.drawRect(15,20,15,2,1);
    lcd.drawRect(28,20,2,10,1);
    lcd.drawRect(15,28,15,2,1);
    lcd.drawRect(15,25,2,3,1);

    lcd.printString("NAKE ",34,3);
    lcd.printString("By M.Birney",10,5);
    lcd.drawRect(10,5,65,30,0); // outline for splash

    // need to refresh display after setting pixels

    lcd.refresh();
}



void easySelected() // display when easy is selected
{
    currentDifficulty=EASY;
    lcd.clear();
    lcd.printString("Please Select",2,0);
    lcd.printString("Difficulty:",2,1);
    lcd.printString("Easy",20,3);
    lcd.printString("Medium",20,4);
    lcd.printString("Hard",20,5);

    lcd.drawCircle(10,27,2,1);
    lcd.drawCircle(10,35,2,0);
    lcd.drawCircle(10,43,2,0);
    lcd.refresh();

    gameSpeed= 1.0/5; // set easy game speed(for game time)
}
void mediumSelected() // display when medium is selected
{
    currentDifficulty=MEDIUM;
    lcd.clear();
    lcd.printString("Please Select",2,0);
    lcd.printString("Difficulty:",2,1);
    lcd.printString("Easy",20,3);
    lcd.printString("Medium",20,4);
    lcd.printString("Hard",20,5);

    lcd.drawCircle(10,27,2,0);
    lcd.drawCircle(10,35,2,1);
    lcd.drawCircle(10,43,2,0);
    gameSpeed=1.0/7; // set medium game speed
    lcd.refresh();
}

void hardSelected() // display when hard is selected
{
    currentDifficulty=HARD;
    lcd.clear();
    lcd.printString("Please Select",2,0);
    lcd.printString("Difficulty:",2,1);
    lcd.printString("Easy",20,3);
    lcd.printString("Medium",20,4);
    lcd.printString("Hard",20,5);

    lcd.drawCircle(10,27,2,0);
    lcd.drawCircle(10,35,2,0);
    lcd.drawCircle(10,43,2,1);
    lcd.refresh();
    gameSpeed=1.0/12; // set hard game speed
}

void classicModeSelected() //display when classic mode selected
{


    selectedGameMode=CLASSIC;
    lcd.clear();
    lcd.printString("Please Select",2,0);
    lcd.printString("Game Mode:",2,1);
    lcd.printString("Classic",20,3);
    lcd.printString("Boundary",20,4);

    lcd.drawCircle(10,27,2,1);
    lcd.drawCircle(10,35,2,0);
    lcd.refresh();

}

void boundaryModeSelected() // display when boundary mode selected
{
    selectedGameMode=BOUNDARY;//update selecected game mode
    lcd.clear();
    lcd.printString("Please Select",2,0);
    lcd.printString("Game Mode:",2,1);
    lcd.printString("Classic",20,3);
    lcd.printString("Boundary",20,4);

    lcd.drawCircle(10,27,2,0);
    lcd.drawCircle(10,35,2,1);
    lcd.refresh();

}


void checkSelectedGameMode()// updates selected game mode menu depending on joystick direction
{

    switch(selectedGameMode) {
        case CLASSIC:

            switch (joystick.direction) {

                case UP:
                    boundaryModeSelected();
                    break;


                case DOWN:
                    boundaryModeSelected();
                    break;
            }
            break;


        case BOUNDARY:

            switch (joystick.direction) {

                case UP:
                    classicModeSelected();
                    break;


                case DOWN:
                    classicModeSelected();
                    break;
            }

            break;
    }
    wait(0.2);

}

void checkSelectedDifficulty()
{

    switch(currentDifficulty) {
        case EASY:

            switch (joystick.direction) {

                case UP:
                    hardSelected();
                    break;


                case DOWN:
                    mediumSelected();
                    break;
            }
            break;


        case MEDIUM:

            switch (joystick.direction) {

                case UP:
                    easySelected();
                    break;


                case DOWN:
                    hardSelected();
                    break;
            }



            break;
        case HARD:
            switch (joystick.direction) {

                case UP:
                    mediumSelected();
                    break;


                case DOWN:
                    easySelected();
                    break;
            }
            break;
    }
    wait(0.2);
}




//for gamePLAY



void startingSnake()
{
    snakeX.resize(5); // ensure that when game is reset snake vectors are resized back to 5
    snakeY.resize(5);//
    snakeX[0]=20; // initial snake position
    snakeX[1]=22;
    snakeX[2]=24;
    snakeX[3]=26;
    snakeX[4]=28;
    snakeY[0]=27;
    snakeY[1]=27;
    snakeY[2]=27;
    snakeY[3]=27;
    snakeY[4]=27;


    for (int i=0; i<5; i++) {

        lcd.drawRect(snakeX[i],snakeY[i],1,1,1); // snake is 2X2 pixels so draw rect with one width and 1 height
    }

}


void randomiseFood()
{
//   http://stackoverflow.com/questions/3383286/create-a-random-even-number-between-range


    srand(time(NULL)); // using time as a seed for rand

    int randomX = 2 * (1 + rand() % (40 - 1 + 1)) ; //generate random even number between 2 and 80 // these are the only coordinates the snake operates in

    int randomY = (2 * (4 + rand() % (22 - 4 + 1))) +1; // generate random even number between 8 and 44 then plus 1 for odd number in range of 9-45


    while(lcd.getPixel(randomX,randomY)==1) { // if that pixel is already filled keep generating till a empty space is found

        int randomX = 2 * (1 + rand() % (40 - 1 + 1)) ;
        int randomY = (2 * (4 + rand() % (22 - 4 + 1))) +1;


    }

    lcd.drawRect(randomX,randomY,1,1,1); // set the food to screen

    foodX[0]=randomX; // update food position
    foodY[0]=randomY;// update food position
    lcd.refresh();
//serial.printf("%d",randomX);
}

void hardBoundary() // use these boundary conditions if gamemode is BOUNDARY
{


    for(int x = 1; x< 83; x++) { // draw 1 to 82 at y=8
        lcd.setPixel(x,8);
    }
    for(int x = 1; x< 83; x++) { // draw 1 to 82 at y=47
        lcd.setPixel(x,47);
    }
    for(int y = 8; y< 48; y++) {// draw 8 tp 47 at x=1
        lcd.setPixel(1,y);
    }
    for(int y = 8; y< 48; y++) {// draw 8 to 47 at x =82
        lcd.setPixel(82,y);
    }

    lcd.refresh();

}
void classicBoundary() // use these boundaries if gamemode is classic
{


    for(int x = 1; x< 83; x++) { // draw 1 to 82 at y=8
        lcd.setPixel(x,8);
    }
    for(int x = 1; x< 83; x+=2) { // draw 1 to 82 at y=47
        lcd.setPixel(x,47);
    }
    for(int y = 8; y< 48; y+=2) {// draw 8 tp 47 at x=1
        lcd.setPixel(1,y);
    }
    for(int y = 8; y< 48; y+=2) {// draw 8 to 47 at x =82
        lcd.setPixel(82,y);
    }

    lcd.refresh();

}


void updateSnakeArray()
{
// code to prevent snake moving in opposite direction to its current path of travel
    if (joystick.direction==LEFT && previousDirection==RIGHT) { // if snake is travelling right but joystick is left
        joystick.direction=RIGHT; // cary on right
    }

    if (joystick.direction==RIGHT && previousDirection==LEFT) { // if snake is travelling left but joystick is right
        joystick.direction=LEFT; //carry on left
    }

    if (joystick.direction==UP && previousDirection==DOWN) {// if snake is travelling down but joystick is up
        joystick.direction=DOWN; // carry on down
    }

    if (joystick.direction==DOWN && previousDirection==UP) { // if snake is travelling up but joystick is down
        joystick.direction=UP; // carry on up
    }

    if (joystick.direction==UNKNOWN || joystick.direction==CENTRE) { // if joystick is unknown or centred
        joystick.direction= previousDirection;  // carry on in previous direction
    }


    lcd.drawRect(snakeX[0],snakeY[0],1,1,2); // delete tail of snake by drawing a clear 2x2 block at first element in vectors

    for (int i =0; i<snakeX.size(); i++) {  // shift elements
        snakeX[i]=snakeX[i + 1]; // apart from head
        snakeY[i]=snakeY[i+ 1];
    }

//code to set new head coordinates
    switch(joystick.direction) {

        case UP:  // if travelling up
            snakeX[snakeX.size()-1]=snakeX[snakeX.size()-2]; // snake new head x coordinate = previous head c coordinate
            snakeY[snakeY.size()-1]=snakeY[snakeY.size()-2]-2; // snake new head y cordinate = previous head y coordinate -2 as it moves up

            if (selectedGameMode==CLASSIC) { // when in classic mode no boundaries so need to check if new head is beyond edge limit
                if(snakeY[snakeY.size()-1] <9) snakeY[snakeY.size()-1]=45; // when travelling up check head against upper edge
            }
            break;

        case DOWN:// if travelling down
            snakeX[snakeX.size()-1]=snakeX[snakeX.size()-2];   // snake new head x coordinate = previous head c coordinate
            snakeY[snakeY.size()-1]=snakeY[snakeY.size()-2]+2;// snake new head y cordinate = previous head y coordinate +2 as it moves down

            if (selectedGameMode==CLASSIC) { // when in classic mode no boundaries so need to check if new head is beyond edge limit
                if(snakeY[snakeY.size()-1] >45) snakeY[snakeY.size()-1]=9; // check new head against bottom edge
            }
            break;

        case LEFT:
            snakeX[snakeX.size()-1]=snakeX[snakeX.size()-2]-2;  // snake new head x coordinate = previous head x coordinate - 2
            snakeY[snakeY.size()-1]=snakeY[snakeY.size()-2];// snake new head y coordinate = previous head y coordinate

            if (selectedGameMode==CLASSIC) { //  when in classic mode no boundaries so need to check if new head is beyond edge limit
                if(snakeX[snakeX.size()-1] <2) snakeX[snakeX.size()-1]=80; // check head against left edge
            }
            break;

        case RIGHT:
            snakeX[snakeX.size()-1]= snakeX[snakeX.size()-2]+2; // snake new head x coordinate = previous head x coordinate + 2
            snakeY[snakeY.size()-1]=snakeY[snakeY.size()-2];// snake new head y coordinate = previous head y coordinate

            if (selectedGameMode==CLASSIC) { //  when in classic mode no boundaries so need to check if new head is beyond edge limit
                if(snakeX[snakeX.size()-1] >80) snakeX[snakeX.size()-1]=2;
            }

            break;

            /* case CENTRE:
                 snakeX[snakeX.size()-1]=snakeX[snakeX.size()-2]+2;
                 snakeY[snakeY.size()-1]=snakeY[snakeY.size()-2];
                 break;
             case UNKNOWN:
                 snakeX[snakeX.size()-1]=snakeX[snakeX.size()-2]+2;
                 snakeY[snakeY.size()-1]=snakeY[snakeY.size()-2];
                 break;*/

    }
}
void gameOverTune() //
{

 //   float frequency[]={440,659};
    //float beat[]={1,1,};
    
      Buzzer.period(1.0/(220)); // set PWM period 1/f  A
Buzzer=0.5; // set duty cycle
wait(0.5); // wait time 
Buzzer.period(1.0/(164)); // E
wait(0.5);//
Buzzer=0;// set duty cycle 0

  //   set PWM period

}

void eatFoodTune(){
      Buzzer.period(1.0/(440)); // set PWM period 1/f  A
Buzzer=0.5; // set duty cycle
wait(0.1); // wait time 
Buzzer=0;// set duty cycle 0
    }
    
void gameOver()
{
    leds=1;
    startGame.detach(); // stop snake game from updating
    lcd.drawRect(snakeX.back(),snakeY.back(),1,1,2); // highlight the point of game over
    wait(0.2);
    lcd.refresh();
    lcd.drawRect(snakeX.back(),snakeY.back(),1,1,1);
    wait(0.3);
    lcd.refresh();
    lcd.drawRect(snakeX.back(),snakeY.back(),1,1,2);
    wait(0.2);
    lcd.refresh();
    lcd.drawRect(snakeX.back(),snakeY.back(),1,1,1);
    wait(0.2);
    lcd.refresh();
    lcd.clear();
    lcd.inverseMode();


    lcd.printString("Your Score" ,12,0);// print score
    lcd.printString("=" ,34,1);

    int updatedScore=score;
    int length = sprintf(buffer,"%2d",updatedScore);

    if (length <= 14)  // if string will fit on display
        lcd.printString(buffer,40,1);

    lcd.printString("Press Reset" ,2,3);
    lcd.printString("Button To" ,10,4);
    lcd.printString("Play Again" ,20,5);

    //
    lcd.refresh();
    gameOverTune();

    //gamePlaying=0;
}

void checkForCollision() // when in BOUNDARY MODE check snake head every move against walls
{

    if (snakeX.back()==0|| snakeX.back()==82 || snakeY.back()==7 ||snakeY.back()>=47) {
        myleds=15;

        gameOver(); // if collision then end game
    }
}

void checkForFood() // check if snake has eaten food
{

    if (snakeX.back()==foodX[0] && snakeY.back()==foodY[0]) {   // if  x and y of head match food


        switch(joystick.direction) {

            case RIGHT:
                snakeX.insert (snakeX.begin() +0,foodX[0]-2 ); // insert new element to tail of snake vector
                snakeY.insert (snakeY.begin() ,foodY[0]);
                //snakeX.push_back(foodX[0]+2);
                // snakeY.push_back(foodY[0]);
                break;
            case LEFT:

                snakeX.insert (snakeX.begin() +0,foodX[0]+2 );
                snakeY.insert (snakeY.begin() ,foodY[0]);

                //  snakeX.push_back(foodX[0]-2);
                // snakeY.push_back(foodY[0]);
                break;


            case UP:

                snakeX.insert (snakeX.begin() +0,foodX[0] );
                snakeY.insert (snakeY.begin() ,foodY[0]+2);
                //  snakeX.push_back(foodX[0]);
                // snakeY.push_back(foodY[0]-2);
                break;

            case DOWN:
                snakeX.insert (snakeX.begin() +0,foodX[0] );
                snakeY.insert (snakeY.begin() ,foodY[0]-2);
                // snakeX.push_back(foodX[0]);
                // snakeY.push_back(foodY[0]+2);
                break;
        }
eatFoodTune();
        lcd.drawRect(snakeX[0],snakeY[0],1,1,1); // draw the new tail

        int updatedScore; // to store updated score


        updatedScore= score+5; // 5 points for every food eaten
        score=updatedScore;
        int length = sprintf(buffer,"%2d",updatedScore);// print updated score to screen

        if (length <= 14)  // if string will fit on display
            lcd.printString(buffer,0,0);
        // lcd.refresh();

        randomiseFood(); // randomise new food

    }


}

void startButtonPressed()
{
    if (gameState==0) { // when at first menu if pressed
        gameState=1; // move to second menu
    }

    else  if (gameState==1) { // when at second menu
        gameState=2; // move to gameplay
    }

}



void updateBrightness()
{
    float ain ;
    ain=pot.read();
    lcd.setBrightness(1-ain);

}
void updateGameISR()
{
    updateGameFlag=1;
}

void printVectorContent()
{

    //for( int i=0; i<snakeX.size(); i++)
    // serial.printf( "%d \n \r"  ,snakeX[i]);

}



void pauseButtonPressed()
{

    if (gamePaused==0) { // if game isnt already paused

        startGame.detach(); // stop game updating

        gamePaused=1; // update paused status
        leds=2; // show amber led

    }

    else { // if game is paused start game
        startGame.attach(&updateGameISR,gameSpeed);

        gamePaused=0;
    }



}

void checkForCollisionWithSelf(int i) // checks snake head with all other parts of snake
{
    if(snakeX.back()==snakeX[i] && snakeY.back()==snakeY[i]) { // if both x and y coordinates of any part of snake match head
        gameOver(); // end game
    }


}
void startUpMenu()
{
    lcd.inverseMode();
    easySelected();
    joystick.direction=UNKNOWN; // unknown so does not start scrolling
    calibrateJoystick();  // get centred values of joystick

}

void ModeMenu()
{
    classicModeSelected();
    joystick.direction=UNKNOWN;// unknown so does not start scrolling
    calibrateJoystick();  // get centred values of joystick
}

void resetButtonPressed()
{

    gameState=0; // when reset button pressed bring back to first menu screen
}


int main()
{
 startButton.setSampleFrequency();
    lcd.init();
    resetButton.mode(PullDown);
    startButton.mode(PullDown);
    button.mode(PullDown);
  //  startButton.rise(&startButtonPressed);
    startButton.attach_asserted( &startButtonPressed );
    resetButton.rise(&resetButtonPressed);
    button.rise(&pauseButtonPressed);
    displaySplash();
    leds=1;
    wait(4);

    while(1) {
        leds=1;// red light
        startUpMenu();
        while (gameState==0) { // wait until user has selected difficulty
            updateJoystick();
            checkSelectedDifficulty();
            updateBrightness();
            //serial.printf("check difficulty loop");

            if (gameState==1) { // initialise mode menu
                ModeMenu();
            }
        }


        while(gameState==1) { // wait unit user has pressed start button and selected a game mode

            updateJoystick();
            checkSelectedGameMode();
            updateBrightness();

        }

        if (gameState==2) { // initialise game

            lcd.clear();
            lcd.normalMode(); // normal colours for gameplay
            if (selectedGameMode==BOUNDARY) hardBoundary(); // depending on game mode selcted set walls
            if (selectedGameMode==CLASSIC) classicBoundary();
            previousDirection=RIGHT; // snake has to be moving right when reset (reset bug fix)
            joystick.direction=RIGHT; // make sure when game reset that joystick is reset to right
            startingSnake(); // print starting snake
            randomiseFood(); // show random food
            int score=5;// initial score is 5
            int length = sprintf(buffer,"%2d",score);
            if (length <= 14)  // if string will fit on display
                lcd.printString(buffer,0,0);
            // lcd.drawRect(0,0,83,7,0);
            lcd.refresh();
            startGame.attach(&updateGameISR,gameSpeed); // start game isr speed determined by difficulty chosen

            while (gameState==2) { // while user doesnt press reset

                //  serial.printf("enter game loop");
                if(updateGameFlag==1) { // check flag
                    leds=4; // green LED when game playing

                    updateGameFlag=0; // reset flag
                    updateJoystick(); // check joystick direction

                    updateSnakeArray(); // update the array
                    for (int i=0; i<snakeX.size(); i++) { // for all elements of snake draw 2x2 image
                        lcd.drawRect(snakeX[i],snakeY[i],1,1,1);

                    }
                    lcd.refresh();
                    previousDirection=joystick.direction; // update previous joystick direction

                    if (selectedGameMode==BOUNDARY) checkForCollision();// if in boundary mode check for a collision

                    checkForFood(); // check if snake has eaten food

                    for (int i=0; i<snakeX.size()-1; i++) {
                        checkForCollisionWithSelf(i);
                        updateBrightness();

                    }

                }

            }
        }



    }
}