Wall dodging game utilising a joystick and Nokia 5110 LCD display

Dependencies:   N5110 mbed

main.cpp

Committer:
el14moh
Date:
2016-04-23
Revision:
7:ca18e8775d8d
Parent:
6:153680563027
Child:
8:b49e0cc362f2

File content as of revision 7:ca18e8775d8d:

/* Joystick
Max Hamilton

My project will be designing a game in which the player must avoid a number
of obstacles coming from different locations for as long as possible

Week 19 Code - Initial test of mbed screen and initilisations
Week 20 Code - Add code to move a player controlled ball around the screen
Week 21 Code - Add code to generate and move an obstacle down the screen

Week 22 Code - Significant progress over easter holiday
    - Diagonal directions added to joystick
    - Collisions implemented. Game over triggers if character hits a wall
    - Walls/obstacles developed greatly
        - Obstacle extended into full wall. Wall structs created and walls travel from all sides of the screen
        - Walls start of appearing from one side of the screen. As time goes on, more will appear from differnt sides, with multiple walls on screen at once
        - Cooldown added to walls to ensure a new one will not instantly appear from a side that another wall has just travelled to.
        - visual warning on screen when walls are about to appear from a new direction
    - Game now keeps track of score and displays it at the end of the game
    - Intro screen added

Week 23 Code    - Brief invincibility added by pressing joybutton
                - invincibilty limit added, indicators added to the side
                - game loops succesfully
                - Menu added with 4 options
                    - DodgeMILD, start the game easy with one wall
                    - DodgeMANIA, start the game hard with all four walls
                    - Help, get instructions on the game
                    - Scores, see the top 3 highscore, will be used for saved scores if SD card is implemented

NOTES   - top wall collision seems to be off
        - horizontal walls still seem to go beyond the left border occasinanly

*/

#include "mbed.h"
#include "N5110.h"
#include "tones.h"

#define DIRECTION_TOLERANCE 0.05    // tolerance of joystick direction
#define PLAYERRADIUS 2              // size of player ball
//#define GAMESPEED 20                // game timer speed
//#define JOYSPEED 20                 // rate at which the joystick is read

//         VCC,    SCE,   RST,   D/C,   MOSI,  SCLK,   LED
N5110 lcd (PTD3 , PTA0 , PTC4 , PTD0 , PTD2 , PTD1 , PTC3);

AnalogIn backlight(PTB2);   // pot to control brightness
DigitalIn button(PTB3);     // joystick button object
AnalogIn xPot(PTB11);       // joystick x direction object
AnalogIn yPot(PTB10);       // joystick y direction object
DigitalOut buzzer(PTA2);    // buzzer object
InterruptIn flick (PTB3);   // interruptin instance of button

Ticker pollJoystick;    // timer to regularly read the joystick
Ticker menu_pollJoystick; // slower ticker to move joystick through menu
Ticker game_timer;      // timer to regularly update the screen

Serial serial(USBTX,USBRX); // Serial for debug

// create enumerated type (0,1,2,3 etc. for direction)
enum DirectionName {
    UP,
    DOWN,
    LEFT,
    RIGHT,
    UPRIGHT,    // Diagonally up + right
    UPLEFT,     // Diagonally up + left
    DOWNRIGHT,  // Diagonally down + right
    DOWNLEFT,   // Diagonally down + left
    CENTRE,
    UNKNOWN
};

typedef struct JoyStick Joystick;   // struct for Joystick
typedef struct Wall Wall;           // struct for Walls
typedef const struct State STyp;
typedef struct Highscore Highscore;

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

struct Wall {
    int x;                      // x-coordinate of wall (realtive to centre of the gap)
    int y;                      // y-coordinate of wall (relative to centre of the gap)
    DirectionName direction;    // Direction the wall travels in
    int random;                 // randomly generated integer to determine when a wall begins to travel
    int cooldown;               // stops a wall respawning before a certain amount of time
    volatile bool moveFlag;     // flag to determine if wall is on screen
    volatile bool genFlag;      // flag to determine if wall has been generated
};

struct State {
    int output;  // output value for current state
    int next_state[3]; // next state (depending on direction 0 - UP, 1 - DOWN)
};

struct Highscore {
    int one;
    int two;
    int three;
};

STyp fsm[4] = {
    {1,{0,1,0}},
    {2,{1,2,0}},
    {4,{2,3,1}},
    {8,{3,3,2}}
};

// struct variable for joystick
Joystick joystick;
// struct variable for moving walls
Wall leftwall;
Wall rightwall;
Wall downwall;
Wall upwall;

Highscore hiscore;

void calibrateJoystick();   // read default positions of the joystick to calibrate later readings
void updateJoystick();      // reads direction the joystick has been moved
void initDisplay();         // initialises the LCD display
void game_timer_isr();      // sets flag for timer interrupt
void initGame();
void moveBall();            // reads joystick direction and moves position of the player
void moveWall();            // moves walls along the screen
void invincible();             // makes player briefly invincible
void checkWallCollision();      // checks for any collisions with wall
void checkBorderCollision();           // checks for any collisions with border
void updateScreen();        // refreshes the screen, redraws player and walls
void warning();             // flashes screen when a new wall is ready to appear
void initSerial();         // sets baud rate for serial
void debug();               // prints for debug purposes
void button_isr();
void blink();               // function to make screen flash (do not put in isr unless breaking the loop)
void printHelp();          // prints game help
void printScores();
void draw_border();
void calculateHighscores();
void initHiscores();

//float refresh_rate = 20; // how often to update display (Hz)
//float g_dt = 1.0F/refresh_rate; // global to store time step (F makes it a float, gets rid of compiler warning)
volatile int g_timer_flag = 0;  // flag for timer interrupt
volatile int game_over_flag = 0;         // flag to signal game over
volatile int g_button_flag = 0;
int printFlag = 0;              // flag for printing
volatile int game_start_flag = 0;        // flag to start the game
int i;                     // x-coordinate value of player
int j;                     // y-coordinate value of player
int counter = 0;                // number of times code has looped
volatile bool mortal = 1;
int invun_cool = 0;
int saves = 5;
int joyspeed = 5;
int score = 0;

int direction;
int state;
int output;

int main()
{
    initSerial();
    srand(time(NULL));                                  // generate seed for random number generation
    calibrateJoystick();
    initDisplay();
    initHiscores();

    wait (1.0);

    flick.rise(&button_isr);
    flick.mode(PullDown);

    lcd.printString("Dodgemania",13,2); // Print game title on screen
    wait (2.5);
    for (int z=0; z<88; z++) {
        lcd.drawCircle(z,20,4,1);

        lcd.clearPixel(z-3,16);
        lcd.clearPixel(z-4,17);
        lcd.clearPixel(z-4,18);
        lcd.clearPixel(z-5,19);
        lcd.clearPixel(z-5,20);
        lcd.clearPixel(z-5,21);
        lcd.clearPixel(z-4,22);
        lcd.clearPixel(z-4,23);
        lcd.clearPixel(z-3,24);
        lcd.refresh();
        wait(0.01);
    }

    lcd.clear();
    wait(0.5);

    blink();

    while(1) {

        joyspeed = 10;
        game_timer.detach();
        game_timer.attach(&game_timer_isr,1.0/joyspeed);

        pollJoystick.detach();
        pollJoystick.attach(&updateJoystick,1.0/joyspeed);  // read joystick (JOYSPEED) times per second

        state = 0;


        while(game_start_flag == 0) // ticker interrupt
            if (g_timer_flag) {
                g_timer_flag = 0;  // clear flag
                if (joystick.direction == UP) {
                    direction = 2;
                } else if (joystick.direction == DOWN) {
                    direction = 1;
                } else {
                    direction = 0;
                }
                output = fsm[state].output;
                state = fsm[state].next_state[direction];

                lcd.clear();

                lcd.printString("Dodgemild",12,1);
                lcd.printString("DodgeMANIA!",12,2);
                lcd.printString("Help",12,3);
                lcd.printString("Scores",12,4);

                if (output == 1) {
                    lcd.drawCircle(6,11,2,1);
                } else if (output == 2) {
                    lcd.drawCircle(6,19,2,1);
                } else if (output == 4) {
                    lcd.drawCircle(6,27,2,1);
                } else {
                    lcd.drawCircle(6,35,2,1);
                }
                lcd.refresh();

                if (g_button_flag) {
                    g_button_flag = 0;
                    if (output == 1) {
                        initGame();
                        game_start_flag = 1;
                    } else if (output == 2) {
                        initGame();
                        counter = 1501;
                        game_start_flag = 1;
                    } else if (output == 4) {
                        blink();
                        lcd.clear();
                        printHelp();
                    } else if (output == 8) {
                        blink();
                        lcd.clear();
                        printScores();
                    }
                }
            }
        blink();
        lcd.clear();


// Draw game border
        draw_border();

// Countdown
        wait(0.5);
        lcd.printString("3",40,2);
        wait(0.5);
        lcd.drawRect(10,10,64,28,2);
        lcd.refresh();
        wait(0.5);
        lcd.printString("2",40,2);
        wait(0.5);
        lcd.drawRect(10,10,64,28,2);
        lcd.refresh();
        wait(0.5);
        lcd.printString("1",40,2);
        wait(0.5);
        lcd.drawRect(10,10,64,28,2);
        lcd.refresh();
        wait(0.5);
        lcd.drawRect(10,10,64,28,2);
        lcd.refresh();
        lcd.printString("Go!",36,2);
        wait(0.5);
        lcd.drawRect(10,10,64,28,2);
        lcd.refresh();

        joyspeed = 20;
        game_timer.detach();
        game_timer.attach(&game_timer_isr,1.0/joyspeed);
        pollJoystick.detach();
        pollJoystick.attach(&updateJoystick,1.0/joyspeed);

        while (game_over_flag == 0) {

            if ( g_timer_flag ) {  // ticker interrupt
                g_timer_flag = 0;  // clear flag
                moveWall();
                moveBall();
                invincible();
                checkBorderCollision();
                if (mortal) {
                    checkWallCollision();
                }
                updateScreen();
                warning();
                debug();

                counter++; // increment counter each cycle (approx. 20 points a second)
                score++; // this is seperate from counter for the purposes of keeping score 0 while all walls are present on DodgeMANIA

                // wall cooldowns increased. N.B these are set to 0 when a wall finishes moving
                leftwall.cooldown++;
                rightwall.cooldown++;
                downwall.cooldown++;
                upwall.cooldown++;
            }
            sleep();
        }

        game_over_flag = 0;

        blink();
        wait(2.0);
        lcd.clear();
        wait(0.2);

        calculateHighscores();

        lcd.printString("Game Over!",14,0);
        lcd.refresh();

        wait(1.0);

        lcd.printString("HiScore:",3,3);
        lcd.printString("Score:",3,2);

        char score_buffer[14];
        char hiscore_buffer[14];
        int length_one = sprintf(score_buffer,"%d",score);
        int length_two = sprintf(hiscore_buffer,"%d",hiscore.one);
        if (score <= 9999) { // 9999 is highest number that fits on screen
            lcd.printString(score_buffer,54,2);
            lcd.printString(hiscore_buffer,54,3);
        } else {
            lcd.printString ("Wow!",54,2); // if score is too large to fit in box
            lcd.printString ("Wow!",54,3);
        }

        lcd.refresh();

        if (score >= hiscore.one) {
            wait(1.0);
            lcd.printString("HIGHSCORE!",13,5);
            wait(2.0);
            lcd.printString("CONGRATS!!",13,5);
            wait(2.0);
        } else {
            wait(5.0);
        }
        blink();
        lcd.clear();
        game_start_flag = 0;

//return 0;
    }

}

void moveBall()   // reads joystick direction and moves position of the player
{
    if (joystick.direction == UP) {
        j-=1;
    } else if (joystick.direction == DOWN) {
        j+=1;
    } else if (joystick.direction == LEFT) {
        i-=1;
    } else if (joystick.direction == RIGHT) {
        i+=1;
    } else if (joystick.direction == UPRIGHT) {
        j-=1;
        i+=1;
    } else if (joystick.direction == UPLEFT) {
        j-=1;
        i-=1;
    } else if (joystick.direction == DOWNRIGHT) {
        j+=1;
        i+=1;
    } else if (joystick.direction == DOWNLEFT) {
        j+=1;
        i-=1;
    }
}

void moveWall()   // moves walls along the screen
{
    leftwall.random = rand()%20;
    rightwall.random = rand()%20;
    downwall.random = rand()%50;
    upwall.random = rand()%50;

    // LEFT WALL
    if (leftwall.moveFlag == 1) {           // if wall is moving
        leftwall.x-=1;                          // move wall left
        if (leftwall.x<8) {                     // if wall hits a border
            leftwall.moveFlag = 0;                  // stop wall moving
            leftwall.cooldown = 0;                  // reset the cooldown for the wall
        }
    } else {                              // if wall has stopped moving
        if (leftwall.genFlag == 0) {            // if a new wall HASN'T been generated
            leftwall.y = rand() % 27+8;             // make new random y-coordinate
            leftwall.x = 82;                        // reset x-coordinate to rightmost position
            leftwall.genFlag = 1;                   // wall has been generated
        } else {                                // if a new wall HAS been generated
            if (leftwall.cooldown > 80) {          // if a new wall hasnt started moving in 4 seconds, force it to move
                leftwall.moveFlag = 1;
                leftwall.genFlag = 0;
            } else if ((leftwall.random == 1)&&(rightwall.cooldown > 60)) {      // if wall starts moving again
                leftwall.moveFlag = 1;                  // start wall moving
                leftwall.genFlag = 0;                   // clear 'wall generated' flag
            } else {                                // else if wall has not started moving again
                leftwall.moveFlag = 0;              // wall is stopped
            }
        }
    }

// RIGHT WALL
    if (counter > 200) {
        if (rightwall.moveFlag == 1) {
            rightwall.x+=1;
            if (rightwall.x>80) {
                rightwall.moveFlag = 0;
                rightwall.cooldown = 0;
            }
        } else {
            if ((rightwall.genFlag == 0)) {
                rightwall.y = rand() % 27+8;
                rightwall.x = 6;
                rightwall.genFlag = 1;
            } else {
                if (rightwall.cooldown > 80) {
                    rightwall.moveFlag = 1;
                    rightwall.genFlag = 0;
                } else if ((rightwall.random == 1) && (leftwall.cooldown > 60)) {
                    rightwall.moveFlag = 1;
                    rightwall.genFlag = 0;
                } else {
                    rightwall.moveFlag = 0;
                }
            }
        }
    }

// DOWN WALL
    if (counter > 600) {
        if (downwall.moveFlag == 1) {
            if (upwall.cooldown > 60) {
                if (counter % 2 == 1) { // horizontal walls move half the speed of vertical walls
                    downwall.y+=1;
                }
            }
            if (downwall.y>44) {
                downwall.moveFlag = 0;
            }
        } else {
            if (downwall.genFlag == 0) {
                downwall.x = rand() % 58+13;
                downwall.y = 1;
                downwall.genFlag = 1;
            } else {
                if (downwall.cooldown > 80) {
                    downwall.moveFlag = 1;
                    downwall.genFlag = 0;
                } else if ((downwall.random == 1)&&(upwall.cooldown > 60)) {
                    downwall.moveFlag = 1;
                    downwall.genFlag = 0;
                } else {
                    downwall.moveFlag = 0;
                }
            }
        }
    }

// UP WALL
    if (counter > 1500) {
        if (upwall.moveFlag == 1) {
            if (downwall.cooldown > 60) {
                if (counter % 2 == 1) { // horizontal walls move half the speed of vertical walls
                    upwall.y-=1;
                }
            }
            if (upwall.y<3) {
                upwall.moveFlag = 0;
            }
        } else {
            if (upwall.genFlag == 0) {
                upwall.x = rand() % 58+13;
                upwall.y = 46;
                upwall.genFlag = 1;
            } else {
                if (upwall.cooldown > 80) {
                    upwall.moveFlag = 1;
                    upwall.genFlag = 0;
                } else if ((upwall.random == 1)&&(downwall.cooldown > 60)) {
                    upwall.moveFlag = 1;
                    upwall.genFlag = 0;
                } else {
                    upwall.moveFlag = 0;
                }
            }
        }
    }
}
void checkBorderCollision()
{
    // if floor
    if ( j >= 47 - (PLAYERRADIUS+3)) {
        j = 47 - (PLAYERRADIUS+3);
    }

    // if roof
    if ( j <= (PLAYERRADIUS+3)) {
        j = (PLAYERRADIUS+3);
    }

    // if right wall
    if ( i >= 83 - (PLAYERRADIUS+3)) {
        i = 83 - (PLAYERRADIUS+3);
    }

    // if left wall
    if ( i <= (PLAYERRADIUS+8)) {
        i = (PLAYERRADIUS+8);
    }
}

void invincible()
{
    if (g_button_flag) {
        g_button_flag = 0;
        if ((saves > 0) && (mortal == true)) { // saves are available and not currently used
            invun_cool=0;
            mortal = false;
            invun_cool++;
            saves--;
        }
    }
    if (mortal == false) {
        invun_cool++;
        if (invun_cool > 30) { // ticker called 20 times per second, therefore 1.5s of invincibility
            mortal = true;
            invun_cool=0;
        }
    }
}

void checkWallCollision()   // checks for any collisions (i.e. player has hit a wall or side of the screen)
{

    // LEFT WALL
    if ((((i - PLAYERRADIUS) <= leftwall.x) && (leftwall.x <= (i + PLAYERRADIUS))) && (j > (leftwall.y+3) || j < (leftwall.y-3))) {
        game_over_flag = 1;
    }

    // RIGHT WALL
    if ((((i - PLAYERRADIUS) <= rightwall.x) && (rightwall.x <= (i + PLAYERRADIUS))) && (j > (rightwall.y+3) || j < (rightwall.y-3))) {
        game_over_flag = 1;
    }

    // DOWN WALL
    if ((((j - PLAYERRADIUS) <= downwall.y) && (downwall.y <= (j - PLAYERRADIUS))) && (i > (downwall.x+9) || i < (downwall.x-9))) { // if player x is 4 or more than wall x, it has missed the gap
        game_over_flag = 1;
    }

    // UP WALL
    if (((j + PLAYERRADIUS) == upwall.y || (j - PLAYERRADIUS) == upwall.y) && (i > (upwall.x+9) || i < (upwall.x-9))) { // if player x is 4 or more than wall x, it has missed the gap
        game_over_flag = 1;
    }
}

void updateScreen()   // refreshes the screen, redraws player and walls
{
    lcd.clear();
    if (mortal) {
        lcd.drawCircle(i,j,PLAYERRADIUS,1);
    } else {
        if (counter % 2 == 0) {
            lcd.drawCircle(i,j,PLAYERRADIUS,1);
        }
    }
    lcd.refresh();  // update display

    if (saves > 0) {
        lcd.drawCircle(2,7,1,1);
    }
    if (saves > 1) {
        lcd.drawCircle(2,15,1,1);
    }
    if (saves > 2) {
        lcd.drawCircle(2,23,1,1);
    }
    if (saves > 3) {
        lcd.drawCircle(2,31,1,1);
    }
    if (saves > 4) {
        lcd.drawCircle(2,39,1,1);
    }


    // draw Border
    draw_border();

    // draw walls
    // LEFT WALL
    lcd.drawLine(leftwall.x,leftwall.y+5,leftwall.x,44,1);
    lcd.drawLine(leftwall.x,leftwall.y-5,leftwall.x,3,1);
    lcd.drawLine(leftwall.x+1,leftwall.y+5,leftwall.x+1,44,1);
    lcd.drawLine(leftwall.x+1,leftwall.y-5,leftwall.x+1,3,1);

    // RIGHT WALL
    if (counter > 200) {
        lcd.drawLine(rightwall.x,rightwall.y+5,rightwall.x,44,1);
        lcd.drawLine(rightwall.x,rightwall.y-5,rightwall.x,3,1);
        lcd.drawLine(rightwall.x-1,rightwall.y+5,rightwall.x-1,44,1);
        lcd.drawLine(rightwall.x-1,rightwall.y-5,rightwall.x-1,3,1);
    }

    // DOWN WALL
    if (counter > 600) {
        lcd.drawLine(downwall.x+11,downwall.y,80,downwall.y,1);
        lcd.drawLine(downwall.x-11,downwall.y,8,downwall.y,1);
        lcd.drawLine(downwall.x+11,downwall.y-1,80,downwall.y-1,1);
        lcd.drawLine(downwall.x-11,downwall.y-1,8,downwall.y-1,1);
    }

    // UP WALL
    if (counter > 1500) {
        lcd.drawLine(upwall.x+11,upwall.y,80,upwall.y,1);
        lcd.drawLine(upwall.x-11,upwall.y,8,upwall.y,1);
        lcd.drawLine(upwall.x+11,upwall.y+1,80,upwall.y+1,1);
        lcd.drawLine(upwall.x-11,upwall.y+1,8,upwall.y+1,1);
    }

    lcd.refresh();
}

void warning ()
{
    if (counter == 170) {
        lcd.inverseMode();
    } else if (counter == 570) {
        lcd.inverseMode();
    } else if (counter == 1470) {
        lcd.inverseMode();
    } else {
        lcd.normalMode();
    }
}

void game_timer_isr()   // sets flag for timer interrupt
{
    g_timer_flag = 1;
}

void button_isr()
{
    g_button_flag = 1;
}

void initDisplay()   // initialises the LCD display
{
    lcd.init();
    lcd.normalMode();
    lcd.setBrightness(1.0F-backlight); // brightness pot on PCB is soldered in the wrong direction, (1.0F-backlight) inverts the reading
}

void calibrateJoystick()    // read default positions of the joystick to calibrate later readings
{
    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()   // reads direction the joystick has been moved
{
    // 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 = LEFT;
    } else if ( joystick.x < DIRECTION_TOLERANCE && fabs(joystick.y) < DIRECTION_TOLERANCE) {
        joystick.direction = RIGHT;
    } else if ( joystick.y > DIRECTION_TOLERANCE && joystick.x > DIRECTION_TOLERANCE) {
        joystick.direction = UPLEFT;
    } else if ( joystick.y > DIRECTION_TOLERANCE && joystick.x < DIRECTION_TOLERANCE) {
        joystick.direction = UPRIGHT;
    } else if ( joystick.x > DIRECTION_TOLERANCE && joystick.y < DIRECTION_TOLERANCE) {
        joystick.direction = DOWNLEFT;
    } else if ( joystick.x < DIRECTION_TOLERANCE && joystick.y < DIRECTION_TOLERANCE) {
        joystick.direction = DOWNRIGHT;
    }

    else {
        joystick.direction = UNKNOWN;
    }


    printFlag = 1;  // set flag for printing
}

void debug()   // prints for debug purposes
{
    if (printFlag) {  // if flag set, clear flag and print joystick values to serial port
        printFlag = 0;
        /*
        if (joystick.direction == UP)
            serial.printf(" UP\n");
        if (joystick.direction == DOWN)
            serial.printf(" DOWN\n");
        if (joystick.direction == LEFT)
            serial.printf(" LEFT\n");
        if (joystick.direction == RIGHT)
            serial.printf(" RIGHT\n");
        if (joystick.direction == UPRIGHT)
            serial.printf(" UPRIGHT\n");    // Diagonally up + right
        if (joystick.direction == UPLEFT)
            serial.printf(" UPLEFT\n");     // Diagonally up + left
        if (joystick.direction == DOWNRIGHT)
            serial.printf(" DOWNRIGHT\n");  // Diagonally down + right
        if (joystick.direction == DOWNLEFT)
            serial.printf(" DOWNLEFT\n");   // Diagonally down + left
        if (joystick.direction == CENTRE)
            serial.printf(" CENTRE\n");
        if (joystick.direction == UNKNOWN)
            serial.printf(" Unsupported direction\n");
            */
        serial.printf("saves = %d \n",saves);
        //serial.printf("Right-wall Cooldown = %d \n",rightwall.cooldown);
        //serial.printf("counter = %d \n",counter);

    }
}

void initSerial()   // sets baud rate for serial
{
    // set to highest baud - ensure terminal software matches
    serial.baud(115200);
}
void initGame()
{

    g_button_flag = 0;
    leftwall.y = rand() % 27+8;
    leftwall.x = 0;
    rightwall.y = rand() % 58+13;
    rightwall.x = 6;
    downwall.x = rand() % 27+8;
    downwall.y = 0;
    upwall.x = rand() % 5814;
    upwall.y = 0;
    rightwall.cooldown = 61;
    counter = 0;
    score = 0;

    saves = 5;

    i = 42;
    j = 24;

}

void blink() // command for brief flash
{
    lcd.inverseMode();
    wait(0.2);
    lcd.normalMode();
}

void printHelp()
{
    lcd.printString("Try and",0,1);
    lcd.printString("survive for",0,2);
    lcd.printString("as long as ",0,3);
    lcd.printString("possible!",0,4);
    wait(4);

    blink();
    lcd.clear();

    lcd.printString("Get through",0,1);
    lcd.printString("the holes in",0,2);
    lcd.printString("the walls with",0,3);
    lcd.printString("the joystick",0,4);
    wait(4);

    blink();
    lcd.clear();

    lcd.printString("Press the",0,1);
    lcd.printString("stick to",0,2);
    lcd.printString("be briefly",0,3);
    lcd.printString("untouchable",0,4);
    wait(4);

    blink();
    lcd.clear();

    lcd.printString("But don't use",0,1);
    lcd.printString("it too much!",0,2);
    lcd.printString("you can only",0,3);
    lcd.printString("do it 5 times",0,4);
    wait(4);

    blink();
    lcd.clear();

    lcd.printString("Lastly, keep",0,1);
    lcd.printString("an eye on the",0,2);
    lcd.printString("borders of",0,3);
    lcd.printString("the screen...",0,4);
    wait(4);

    blink();
    lcd.clear();

    lcd.printString("See where the",0,1);
    lcd.printString("holes are",0,2);
    lcd.printString("before the",0,3);
    lcd.printString("walls move!",0,4);
    wait(4);

    blink();
    lcd.clear();
}

void printScores()
{
    lcd.printString("Highscores",13,0);
    lcd.printString("1st:",6,2);
    lcd.printString("2nd:",6,3);
    lcd.printString("3rd:",6,4);
    char first[14];
    char second[14];
    char third[14];
    int one = sprintf(first,"%d",hiscore.one);
    int two = sprintf(second,"%d",hiscore.two);
    int three = sprintf(third,"%d",hiscore.three);
   
        lcd.printString(first,30,2);
    
   
        lcd.printString(second,30,3);
    
    
        lcd.printString(third,30,4);
    
    lcd.refresh();
    wait(4);
    blink();
    lcd.clear();

}

void draw_border()  // draws game border
{
    lcd.drawLine(7,2,81,2,1);
    lcd.drawLine(7,2,7,45,1);
    lcd.drawLine(81,2,81,45,1);
    lcd.drawLine(7,45,81,45,1);

    lcd.drawLine(5,0,83,0,1);
    lcd.drawLine(5,0,5,47,1);
    lcd.drawLine(83,0,83,47,1);
    lcd.drawLine(5,47,83,47,1);

    lcd.refresh();
}

void calculateHighscores()
{
    if (score > hiscore.one) {
        hiscore.three = hiscore.two;
        hiscore.two = hiscore.one;
        hiscore.one = score;
    } else if ((hiscore.one > score)&&(score > hiscore.two)) {
        hiscore.three = hiscore.two;
        hiscore.two = score;
    } else if ((hiscore.two > score)&&(score > hiscore.three)) {
        hiscore.three = score;
    }
}

void initHiscores()
{
    hiscore.one = 0;
    hiscore.two = 0;
    hiscore.three = 0;
}