Code for simple game of snake with four playing modes.

Dependencies:   N5110 PinDetect PowerControl mbed

main.cpp

Committer:
fiifimills
Date:
2015-05-11
Revision:
12:fa654504b51f
Parent:
11:f421defe6f55

File content as of revision 12:fa654504b51f:

#include "mbed.h"
#include "N5110.h"
#include "PinDetect.h"
#include "PowerControl/PowerControl.h"
#include "PowerControl/EthernetPowerControl.h"
#include <vector>
#include <stdlib.h>     /* srand, rand */
#include <time.h>

/* Code for Snake game

B.F. Mills

April 12th 2015

*/

// change this to alter tolerance of joystick direction
#define DIRECTION_TOLERANCE 0.05

//      vcc, sce, rst, dc, mosi, sck, backlight
N5110 lcd(p7, p8, p9, p10, p11, p13, p26);

// joystick connections
AnalogIn yPot(p15);
AnalogIn xPot(p16);
DigitalIn button(p17);

// leds
BusOut myleds(LED1, LED2, LED3, LED4);

// buttons
PinDetect buttonA(p30);
PinDetect buttonB(p29);

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

DirectionName currentDirection;  // enum variable for current direction of snake's movement
DirectionName previousDirection;  // enum variable for previous direction of snake's movement

// enumerated type for game menu screen
enum GameMenu {
    STARTUP,
    SELECTSPEED,
    GAMEPLAY
};

GameMenu currentGameMenu;  // create game menu variable

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

// struct for a coordinate
struct coordinate {
    int x; // x coordinate
    int y; // y coordinate
};

char buffer [14];  // buffer for printing score to screen

// flags
int printFlag = 0;
int Aflag = 0;          // flag for notification of A button interrupt
int Bflag = 0;           // flag for notification of B button interrupt
int collisionFlag = 0;      // flag for notification of self snake collision
int borderFlag = 0;         // flag for hitting border in insane mode

// global variables
int score = 0;              // score value
bool gameOver;              // boolean variable for game over
int xCentre = 4;            // x coordinate for centre of selection circle
int yCentre = 11;           // y coordinate for centre of selection circle
float speed;            // wait time for moveSnake function

void pressedA()
{
    Aflag = 1;  // set flag when A button is pressed
}

void pressedB()
{
    Bflag = 1;  // set flag when B button is pressed
}

// function prototypes
void calibrateJoystick();
void updateJoystick();
void moveSnake();
void speedSelect();

std::vector<coordinate> Snake;  // create a vector that stores coordinate structs

int main()
{

    calibrateJoystick();  // get centred values of joystick
    pollJoystick.attach(&updateJoystick,1.0/20.0);  // read joystick 20 times per second

    lcd.init();  // initialise display
    lcd.setBrightness(0.5);  // set brightness of backlight

    PHY_PowerDown();   // power down ethernet

    // set button modes to pull down
    buttonA.mode(PullDown);
    buttonB.mode(PullDown);

    buttonA.attach_asserted(&pressedA);  // call pressedA function when button is pressed
    buttonB.attach_asserted(&pressedB);  // call pressedB function when button is presed

    // set sample frequency for buttons (default = 20ms)
    buttonA.setSampleFrequency();
    buttonB.setSampleFrequency();

    currentGameMenu = STARTUP;  // initialise game menu to be splash screen
    gameOver = false; // initialise gameOver as false


    while (1) {  // infinite loop

        if (currentGameMenu == STARTUP) { // start up screen

            // splash screen for startup
            lcd.printString("SNAKE",28,1);
            lcd.printString("PRESS A",21,5);

            // draw snake logo
            lcd.drawRect(26,27,5,5,0); // bottom rectangle of snake
            lcd.drawRect(26,22,5,5,0);  // the rectangle directly above
            lcd.drawRect(31,22,5,5,0);  // the rectangle one to the right
            lcd.drawRect(36,22,5,5,0);  // one to the right
            lcd.drawRect(41,22,5,5,0);  // one to the right
            lcd.drawRect(51,22,5,5,1);  // "food"
            lcd.refresh();
            
            Sleep();  // when flag is not set, go to sleep & wait for interrupt  

            if (Aflag) { // if currentGameMenu is STARTUP and A is pressed,
                Aflag = 0;  // reset flag
                lcd.clear();  // clear screen
                currentGameMenu = SELECTSPEED;  // segue to select speed menu
            }
            
        }

        if (currentGameMenu == SELECTSPEED) {  // select speed menu

            // print menu options to screen
            lcd.printString("SLOW",10,1);
            lcd.printString("MEDIUM",10,3);
            lcd.printString("FAST",10,5);
            lcd.printString("INSANE",46,1);

            lcd.drawCircle(xCentre,yCentre,3,1);  // draw selection circle (starting on SLOW)
            lcd.refresh();
            speedSelect();

            if (Aflag) {  // if currentGameMenu is SELECTSPEED and A is pressed,
                Aflag = 0;  // reset flag
                lcd.clear();  // cleat screen
                currentGameMenu = GAMEPLAY;  // segue to gameplay
            }

            Sleep();  // when flag is not set, got to sleep & wait for interrupt

        }

        if (currentGameMenu == GAMEPLAY) {  // gameplay

            if (gameOver == false) {  // whilst game is not over

                if (xCentre + yCentre == 15) {  // if the addition of the coordinates of the centre of the selection circle = 15 i.e when selecting SLOW
                    speed = 0.20;  // set wait time for moveSnake
                }
                if (xCentre + yCentre == 31) {  // medium
                    speed = 0.12;
                }
                if (xCentre + yCentre == 47) {  // fast
                    speed = 0.04;
                }
                if (xCentre + yCentre == 53) {  // insane
                    speed = 0.02;

                    for (int y=0; y<23 ; y++) {   // set upper border
                        lcd.setPixel(43,y);
                        lcd.refresh();
                    }

                    for (int y=25; y<47; y++) { // set lower border
                        lcd.setPixel(43,y);
                        lcd.refresh();
                    }
                }

                previousDirection = RIGHT;  // initialise first direction to be right

                // set of initial coordinates
                coordinate cord0 = {45, 24};
                coordinate cord1 = {46, 24};
                coordinate cord2 = {47, 24};
                coordinate cord3 = {48, 24};

                // add these coordinates to the snake vector
                Snake.push_back(cord0);
                Snake.push_back(cord1);
                Snake.push_back(cord2);
                Snake.push_back(cord3);

                // set these co-ordinate's pixels
                for (int i=0; i<Snake.size(); i++) {
                    lcd.setPixel(Snake.at(i).x, Snake.at(i).y);
                    lcd.refresh();
                }

                moveSnake();
            }

            if (gameOver == true) {  // if game is over

                // display game over splash screen
                lcd.printString("Game Over!",14,1);
                lcd.printString("Press B",23,4);
                int printScore = sprintf(buffer,"Score = %d",score*5);  // print formatted data to buffer
                if (printScore <= 14) { // if string fits on display
                    lcd.printString(buffer,16,3);
                }
                Snake.clear();  // clear snake vector

                while (1) {  // flash leds
                    myleds = 15;
                    wait(0.5);
                    myleds = 0;
                    wait(0.5);
                    if (Bflag) // if B is pressed, break out of infinite loop
                        break;
                }

                if (Bflag) {  // if B is pressed,
                    Bflag = 0;  // reset flag
                    collisionFlag = 0;  // reset collision flag
                    score = 0;  // reset score
                    myleds = 0;  // switch off leds
                    borderFlag = 0;
                    gameOver = false;  // reset gameOver to false
                    lcd.clear();  // clear the screen
                    currentGameMenu = STARTUP;  // go back to startup screen
                }

            }
        }

    }
}

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

    // set flag for printing
    printFlag = 1;
}

void moveSnake()
{
    srand(time(NULL));  // use time as seed for rand

    int foodX = rand()%82+1;  // random x coordinate between 1-82
    int foodY = rand()%46+1;  // random y coordinate between 1-46

    // draw border
    lcd.drawRect(0,0,83,47,0);  // x-origin, y-origin, width, height, fill
    lcd.refresh();

    if (!lcd.getPixel(foodX,foodY))  // if pixel is not already set
        lcd.setPixel(foodX,foodY);  // set pixel of this coordinate (used for food)
        lcd.refresh();

    while (1) {  // infinite loop

        if (joystick.direction == CENTRE) {  // if the joystick is in the centre
            currentDirection = previousDirection;  // current direction is the previous direction of movement
        }
        if (joystick.direction == UNKNOWN) {  // if the joystick's direction is unknown
            currentDirection = previousDirection;  // current direction is the previous direction
        }

        // if joystick direction is left, right, up or down, set the current direction to be the joystick's direction
        if (joystick.direction == LEFT) {
            currentDirection = joystick.direction;
        }
        if (joystick.direction == RIGHT) {
            currentDirection = joystick.direction;
        }
        if (joystick.direction == UP) {
            currentDirection = joystick.direction;
        }
        if (joystick.direction == DOWN) {
            currentDirection = joystick.direction;
        }

        lcd.clearPixel(Snake.front().x, Snake.front().y);  // clear the tail pixel of snake

        for (int i=0; i<Snake.size()-1; i++) {  // shift along each element in the vector so that..
            Snake.at(i) = Snake.at(i+1);  // value of element Snake[0] = Snake[1], Snake[1] = Snake[2] etc.
        }

        switch (currentDirection) {  // check the current direction

            case UP: // if up
                if (previousDirection == DOWN) {  // check if the previous direction was opposite to the current direction
                    currentDirection = DOWN;  // if so, move in the previous direction
                    Snake.back().y++; // increment appropiate coordinate value
                } else { // if the previous direction wasn't opposite to the current direction,
                    previousDirection = UP;  // set the previous direction
                    Snake.back().y--;  // increment appropiate coordinate value
                }
                break;

            case DOWN:
                if (previousDirection == UP) {
                    currentDirection = UP;
                    Snake.back().y--;
                } else {
                    previousDirection = DOWN;
                    Snake.back().y++;
                }
                break;

            case LEFT:
                if (previousDirection == RIGHT) {
                    currentDirection = RIGHT;
                    Snake.back().x++;
                } else {
                    previousDirection = LEFT;
                    Snake.back().x--;
                }
                break;

            case RIGHT:
                if (previousDirection == LEFT) {
                    currentDirection = LEFT;
                    Snake.back().x--;
                } else {
                    previousDirection = RIGHT;
                    Snake.back().x++;
                }
                break;

            default:
                break;
        }

        if ((Snake.back().x == foodX) && (Snake.back().y == foodY)) {  // if the snake head hits the food

            switch (currentDirection) {  // check the current direction

                case UP:
                    Snake.push_back(Snake.back());  // add an element, which is equal to the penultimate element, to the end of the vector
                    Snake.back().y--;  // increment the appropiate coordinate of this new element for the direction of movement
                    foodX = rand()%82 + 1;  // x coordinate of food is now a new random number between 1-83
                    foodY = rand()%46 + 1;  // y coordinate of food is now a new random number between 1-47

                    if (!lcd.getPixel(foodX,foodY)) {
                        lcd.setPixel(foodX,foodY);
                        lcd.refresh();
                    }

                    score++;  // increment score value

                    // flash led
                    myleds = 1;
                    wait(0.2);
                    myleds = 0;
                    break;

                case DOWN:
                    Snake.push_back(Snake.back());
                    Snake.back().y++;
                    foodX = rand()%82 + 1;
                    foodY = rand()%46 + 1;

                    if (!lcd.getPixel(foodX,foodY)) {
                        lcd.setPixel(foodX,foodY);
                        lcd.refresh();
                    }
                    score++;

                    myleds = 1;
                    wait(0.2);
                    myleds = 0;
                    break;

                case LEFT:
                    Snake.push_back(Snake.back());
                    Snake.back().x--;
                    foodX = rand()%82 + 1;
                    foodY = rand()%46 + 1;

                    if (!lcd.getPixel(foodX,foodY)) {
                        lcd.setPixel(foodX,foodY);
                        lcd.refresh();
                    }
                    score++;

                    myleds = 1;
                    wait(0.2);
                    myleds = 0;
                    break;

                case RIGHT:
                    Snake.push_back(Snake.back());
                    Snake.back().x++;
                    foodX = rand()%82 + 1;
                    foodY = rand()%46 + 1;

                    if (!lcd.getPixel(foodX,foodY)) {
                        lcd.setPixel(foodX,foodY);
                        lcd.refresh();
                    }
                    score++;

                    myleds = 1;
                    wait(0.2);
                    myleds = 0;
                    break;

                default:
                    break;
            }

        }


        for (int i = 0; i < Snake.size()-1; i++) {  // iterate through all elements in the snake vector
            if ((Snake.back().x == Snake.at(i).x) && (Snake.back().y == Snake.at(i).y)) {  // if snake head touches any other part of the snake
                collisionFlag = 1;  // set colision flag
                gameOver = true; // set gameOver to true
            }

        }

        if (collisionFlag) break;  // if collision flag is set, break out of infinite loop

        if (Snake.back().x > 82) {  // if snakeHead's x coordinate is 83 or larger (i.e touching the right hand side border)
            gameOver = true;  // game over is true
            break;  // break out of infinite loop
        }

        if (Snake.back().x < 1) {  // touching left hand side border
            gameOver = true;
            break;
        }

        if (Snake.back().y > 46) {  // touching top border
            gameOver = true;
            break;
        }

        if (Snake.back().y < 1) {  // touching bottom border
            gameOver = true;
            break;
        }

        if (xCentre + yCentre == 53) {  // insane

            if ((Snake.back().x == 43) && (Snake.back().y < 23)) {  // if snake touches upper border
                gameOver = true;  // game over is true
                borderFlag = 1;  // set border flag
            }

            if ((Snake.back().x == 43) && (Snake.back().y > 24)) {  // if snake touches lower border
                gameOver = true;  // game over is true
                borderFlag = 1;  // set border flag
            }
        }

        if (borderFlag) break;  // if border flag is set, break out of infinite loop

        lcd.setPixel(Snake.back().x, Snake.back().y);  // set new snakeHead's pixel
        lcd.refresh();
        wait(speed);

    }
}

void speedSelect()
{
    if (joystick.direction == DOWN) {  // if the joystick is moved down

        if (xCentre == 4 && yCentre == 11) {  // i.e. when the selection circle is on SLOW
            lcd.clear(); // clear the screen

            // print options
            lcd.printString("SLOW",10,1);
            lcd.printString("MEDIUM",10,3);
            lcd.printString("FAST",10,5);
            lcd.printString("INSANE",46,1);

            yCentre = 27;  // set new y value for centre of selection circle
            lcd.drawCircle(xCentre,yCentre,3,1);  // draw new selection circle
            lcd.refresh();
            wait(0.3);
        }

        else if (xCentre == 4 && yCentre == 27) {  // medium
            lcd.clear();
            lcd.printString("SLOW",10,1);
            lcd.printString("MEDIUM",10,3);
            lcd.printString("FAST",10,5);
            lcd.printString("INSANE!",46,1);
            yCentre = 43;
            lcd.drawCircle(xCentre,yCentre,3,1);
            lcd.refresh();
            wait(0.3);
        }

        else if (xCentre == 4 && yCentre == 43) {  // fast
            lcd.clear();
            lcd.printString("SLOW",10,1);
            lcd.printString("MEDIUM",10,3);
            lcd.printString("FAST",10,5);
            lcd.printString("INSANE",46,1);
            xCentre = 42;
            yCentre = 11;
            lcd.drawCircle(xCentre,yCentre,3,1);
            lcd.refresh();
            wait(0.3);
        }
    }

    if (joystick.direction == UP) {

        if (xCentre == 42 && yCentre == 11) {  // insane
            lcd.clear();
            lcd.printString("SLOW",10,1);
            lcd.printString("MEDIUM",10,3);
            lcd.printString("FAST",10,5);
            lcd.printString("INSANE",46,1);
            xCentre = 4;
            yCentre = 43;
            lcd.drawCircle(xCentre,yCentre,3,1);
            lcd.refresh();
            wait(0.3);
        }

        else if (xCentre == 4 && yCentre == 43) {  // fast
            lcd.clear();
            lcd.printString("SLOW",10,1);
            lcd.printString("MEDIUM",10,3);
            lcd.printString("FAST",10,5);
            lcd.printString("INSANE!",46,1);
            yCentre = 27;
            lcd.drawCircle(xCentre,yCentre,3,1);
            lcd.refresh();
            wait(0.3);
        }

        else if (xCentre == 4 && yCentre == 27) {  // medium
            lcd.clear();
            lcd.printString("SLOW",10,1);
            lcd.printString("MEDIUM",10,3);
            lcd.printString("FAST",10,5);
            lcd.printString("INSANE",46,1);
            yCentre = 11;
            lcd.drawCircle(xCentre,yCentre,3,1);
            lcd.refresh();
            wait(0.3);
        }
    }




}