Wall dodging game utilising a joystick and Nokia 5110 LCD display

Dependencies:   N5110 mbed

Files at this revision

API Documentation at this revision

Comitter:
el14moh
Date:
Thu May 05 14:52:59 2016 +0000
Parent:
9:ca800196baeb
Commit message:
Code finished and ready for submission

Changed in this revision

main.cpp Show annotated file Show diff for this revision Revisions of this file
main.h Show annotated file Show diff for this revision Revisions of this file
--- a/main.cpp	Sun Apr 24 16:17:55 2016 +0000
+++ b/main.cpp	Thu May 05 14:52:59 2016 +0000
@@ -1,430 +1,33 @@
-/* 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 MUTE 0                      // 0 - buzzer plays, 1 - buzzer muted
-
-//         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
-PwmOut 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();
-void playNote(int freq, float time);
-
-//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;
+#include "main.h"
 
 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();
+    initSerial();               // Initialises serial port
+    srand(time(NULL));          // Generate seed for random number generation
+    calibrateJoystick();        // Zeroes current position of joystick
+    button.rise(&button_isr);   // Attach button to ISR function
+    button.mode(PullDown);      // Select button mode
+    initDisplay();              // Initialise LCD display
+    initHiscores();             // Initialise highscores
+    introScreen();              // Plays intro animation with title
+    game_running = 0;
 
     while(1) {
-        g_button_flag = 0;
-
-        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 (((direction == 1)&&(output != 8))||((direction == 2)&&(output != 1))) {
-                    playNote(NOTE_G2,0.05);
-                }
+        g_button_flag = 0;      // force flag off to prevent menu items being accidently selected
+        setTickers(10);         // calls ticker functions 10 times per second (slower speed to make menu control easier)
+        state = 0;              // start on top menu item by default
 
-                if (g_button_flag) {
-                    if (output == 1) {
-                        g_button_flag = 0;
-                        playNote(NOTE_B2,0.1);
-                        initGame();
-                        game_start_flag = 1;
-                    } else if (output == 2) {
-                        g_button_flag = 0;
-                        playNote(NOTE_B2,0.1);
-                        initGame();
-                        counter = 1501;
-                        game_start_flag = 1;
-                    } else if (output == 4) {
-                        g_button_flag = 0;
-                        playNote(NOTE_B2,0.1);
-                        blink();
-                        lcd.clear();
-                        printHelp();
-                    } else if (output == 8) {
-                        g_button_flag = 0;
-                        playNote(NOTE_B2,0.1);
-                        blink();
-                        lcd.clear();
-                        printScores();
-                    }
-                }
-            }
-        blink();
-        lcd.clear();
-// Draw game border
-        draw_border();
-// Countdown
-        wait(0.5);
-        lcd.printString("3",40,2);
-        playNote(NOTE_C4,0.2);
-        wait(0.3);
-        lcd.drawRect(10,10,64,28,2);
-        lcd.refresh();
-        wait(0.5);
-        lcd.printString("2",40,2);
-        playNote(NOTE_C4,0.2);
-        wait(0.3);
-        lcd.drawRect(10,10,64,28,2);
-        lcd.refresh();
-        wait(0.5);
-        lcd.printString("1",40,2);
-        playNote(NOTE_C4,0.2);
-        wait(0.3);
-        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);
-        playNote(NOTE_G4,0.5);
-        lcd.drawRect(10,10,64,28,2);
-        lcd.refresh();
+        menu();                 // Brings up game menu (Help and Scores contained in menu() function, selecting a gameplay option moves on from menu)
 
-        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();
-                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();
-        playNote(NOTE_E4,0.1);
-        playNote(NOTE_C4,0.1);
-        playNote(NOTE_A3,0.1);
-        playNote(NOTE_A2,0.3);
-        wait(0.8);
-        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);
+        setTickers(20);         // Calls ticker functions 20 times per second
 
-        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);
-            playNote(NOTE_E3,0.1);
-            wait(0.1);
-            playNote(NOTE_C3,0.05);
-            wait(0.05);
-            playNote(NOTE_C3,0.05);
-            wait(0.05);
-            playNote(NOTE_C3,0.05);
-            wait(0.15);
-            playNote(NOTE_G3,0.1);
-            wait(0.1);
-            playNote(NOTE_E3,0.1);
-            wait(0.1);
-            playNote(NOTE_G3,0.1);
-            wait(0.1);
-            playNote(NOTE_C4,0.5);
-            wait(0.1);
-            lcd.printString("CONGRATS!!",13,5);
-            playNote(NOTE_E2,0.2);
-
-        } else {
-            playNote(NOTE_A2,0.05);
-            wait(0.05);
-            playNote(NOTE_A2,0.05);
-            wait(0.05);
-            playNote(NOTE_A2,0.05);
-            wait(0.05);
-            playNote(NOTE_A2,0.3);
-        }
-        lcd.refresh();
-        while(1) {
-            if (g_button_flag) {
-                g_button_flag = 0;
-                playNote(NOTE_E2,0.1);
-                blink();
-                lcd.clear();
-                break;
-            }
-        }
-        game_start_flag = 0;
-
-//return 0;
+        playGame();             // Begins gameplay
+        resultsScreen();        // Show final player score and previous highscore
     }
-
 }
 
-void moveBall()   // reads joystick direction and moves position of the player
+void moveBall()   // Reads joystick direction and moves position of the player
 {
     if (joystick.direction == UP) {
         j-=1;
@@ -449,50 +52,51 @@
     }
 }
 
-void moveWall()   // moves walls along the screen
+void moveWall()   // Moves walls along the screen
 {
-    leftwall.random = rand()%20;
+    // Random variables to determine if wall moves this loop or not
+    leftwall.random = rand()%20;    // 1/20 chance
     rightwall.random = rand()%20;
-    downwall.random = rand()%50;
+    downwall.random = rand()%50;    // 1/50 chance
     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
+    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
+    } 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 (note - range of random values prevents holes in the walls going beyond border)
+            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 new wall hasnt started moving in 4 seconds, force it to move
+                leftwall.moveFlag = 1;                                          // start wall moving
+                leftwall.genFlag = 0;                                           // clear 'wall generated' flag
+            } else if ((leftwall.random == 1)&&(rightwall.cooldown > 60)) { // else 2 second window in which wall may randomly start moving before it is forced to
+                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 (counter > 200) {    // After 10 seconds of gameplay
         if (rightwall.moveFlag == 1) {
-            rightwall.x+=1;
-            if (rightwall.x>80) {
+            rightwall.x+=1;                     // move wall right
+            if (rightwall.x>80) {               // if wall goes off right edge (accounting for border)
                 rightwall.moveFlag = 0;
                 rightwall.cooldown = 0;
             }
         } else {
             if ((rightwall.genFlag == 0)) {
-                rightwall.y = rand() % 27+8;
-                rightwall.x = 6;
+                rightwall.y = rand() % 27+8;    // random y-coordinate (note - range of random values prevents holes in the walls going beyond border)
+                rightwall.x = 6;                // moves wall back to left side
                 rightwall.genFlag = 1;
             } else {
                 if (rightwall.cooldown > 80) {
@@ -509,23 +113,21 @@
     }
 
 // DOWN WALL
-    if (counter > 600) {
+    if (counter > 600) {    // After 30 seconds of gameplay
         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 (counter % 2 == 1) {             // horizontal walls move half the speed of vertical walls
+                downwall.y+=1;                  // move wall down
             }
-            if (downwall.y>44) {
+            if (downwall.y>44) {                // if wall goes off bottom edge (accounting for border)
                 downwall.moveFlag = 0;
             }
         } else {
             if (downwall.genFlag == 0) {
-                downwall.x = rand() % 58+13;
+                downwall.x = rand() % 52+19;    // random x-coordinate (note - range of random values prevents holes in the walls going beyond border)
                 downwall.y = 1;
                 downwall.genFlag = 1;
             } else {
-                if (downwall.cooldown > 80) {
+                if (downwall.cooldown > 120) {  // 6s cooldown
                     downwall.moveFlag = 1;
                     downwall.genFlag = 0;
                 } else if ((downwall.random == 1)&&(upwall.cooldown > 60)) {
@@ -538,24 +140,22 @@
         }
     }
 
-// UP WALL
-    if (counter > 1500) {
+    // UP WALL
+    if (counter > 1200) {   // After 60 seconds of gameplay
         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 (counter % 2 == 1) {             // horizontal walls move half the speed of vertical walls
+                upwall.y-=1;                    // move wall up
             }
-            if (upwall.y<3) {
+            if (upwall.y<3) {                   // if wall goes off bottom edge (accounting for border)
                 upwall.moveFlag = 0;
             }
         } else {
             if (upwall.genFlag == 0) {
-                upwall.x = rand() % 58+13;
+                upwall.x = rand() % 52+19;      // random x-coordinate (note - range of random values prevents holes in the walls going beyond border)
                 upwall.y = 46;
                 upwall.genFlag = 1;
             } else {
-                if (upwall.cooldown > 80) {
+                if (upwall.cooldown > 120) {    // 6s cooldown
                     upwall.moveFlag = 1;
                     upwall.genFlag = 0;
                 } else if ((upwall.random == 1)&&(downwall.cooldown > 60)) {
@@ -568,71 +168,75 @@
         }
     }
 }
-void checkBorderCollision()
+void checkBorderCollision()   // Checks if player has hit border
 {
     // if floor
     if ( j >= 47 - (PLAYERRADIUS+3)) {
-        j = 47 - (PLAYERRADIUS+3);
+        j = 47 - (PLAYERRADIUS+3);  // Forces player position
     }
 
     // if roof
     if ( j <= (PLAYERRADIUS+3)) {
-        j = (PLAYERRADIUS+3);
+        j = (PLAYERRADIUS+3);   // Forces player position
     }
 
     // if right wall
     if ( i >= 83 - (PLAYERRADIUS+3)) {
-        i = 83 - (PLAYERRADIUS+3);
+        i = 83 - (PLAYERRADIUS+3);  // Forces player position
     }
 
     // if left wall
     if ( i <= (PLAYERRADIUS+8)) {
-        i = (PLAYERRADIUS+8);
+        i = (PLAYERRADIUS+8);   // Forces player position
     }
 }
 
-void invincible()
+void invincible()   // Briefly allows player to move through walls
 {
     if (g_button_flag) {
         g_button_flag = 0;
-        if ((saves > 0) && (mortal == true)) { // saves are available and not currently used
-            invun_cool=0;
+        if ((saves > 0) && (mortal == true)) { // Invincibility is available and not currently used
+            saves_cool=0;
             mortal = false;
-            invun_cool++;
+            saves_cool++;
             saves--;
         }
     }
     if (mortal == false) {
-        invun_cool++;
-        if (invun_cool > 30) { // ticker called 20 times per second, therefore 1.5s of invincibility
+        saves_cool++;           // Countdown variable for invincibility
+        if (saves_cool > 30) {  // Ticker called 20 times per second, therefore 1.5s of invincibility
             mortal = true;
-            invun_cool=0;
+            saves_cool=0;       // Resets countdown variable
         }
     }
 }
 
-void checkWallCollision()   // checks for any collisions (i.e. player has hit a wall or side of the screen)
+void checkWallCollision()   // Checks for any collisions (i.e. player has hit a wall or side of the screen)
 {
-    if (mortal) {
+    if (mortal) {   // If player has not triggered invincibility
 
         // LEFT WALL
+        //  (- - - - - - - If wall has passed inside of player radius - - - - - - -)      (- - If player has hit side of wall gap - -)
         if ((((i - PLAYERRADIUS) <= leftwall.x) && (leftwall.x <= (i + PLAYERRADIUS))) && (j > (leftwall.y+3) || j < (leftwall.y-3))) {
-            game_over_flag = 1;
+            game_running = 0;
         }
 
         // RIGHT WALL
+        //  (- - - - - - - If wall has passed inside of player radius - - - - - - -)      (- - If player has hit side of wall gap - -)
         if ((((i - PLAYERRADIUS) <= rightwall.x) && (rightwall.x <= (i + PLAYERRADIUS))) && (j > (rightwall.y+3) || j < (rightwall.y-3))) {
-            game_over_flag = 1;
+            game_running = 0;
         }
 
         // 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;
+        //  (- - - - - - - If wall has passed inside of player radius - - - - - - -)      (- - If player has hit side of wall gap - -)
+        if ((((j - PLAYERRADIUS) <= downwall.y) && (downwall.y-1 <= (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_running = 0;
         }
 
         // 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;
+        //  (- - - - - - - If wall has passed inside of player radius - - - - - - -)      (- - If player has hit side of wall gap - -)
+        if ((((j - PLAYERRADIUS) <= upwall.y+1) && (upwall.y <= (j + PLAYERRADIUS))) && (i > (upwall.x+9) || i < (upwall.x-9))) {
+            game_running = 0;
         }
     }
 }
@@ -643,12 +247,12 @@
     if (mortal) {
         lcd.drawCircle(i,j,PLAYERRADIUS,1);
     } else {
-        if (counter % 2 == 0) {
+        if (counter % 2 == 0) { // Make player blink if invincible
             lcd.drawCircle(i,j,PLAYERRADIUS,1);
         }
     }
-    lcd.refresh();  // update display
 
+    // Draws remaining invincibilty indicators
     if (saves > 0) {
         lcd.drawCircle(2,7,1,1);
     }
@@ -665,11 +269,10 @@
         lcd.drawCircle(2,39,1,1);
     }
 
-
-    // draw Border
+    // Draw border
     draw_border();
 
-    // draw walls
+    // 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);
@@ -677,7 +280,7 @@
     lcd.drawLine(leftwall.x+1,leftwall.y-5,leftwall.x+1,3,1);
 
     // RIGHT WALL
-    if (counter > 200) {
+    if (counter > 200) {    // After 10 seconds of gameplay
         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);
@@ -685,26 +288,22 @@
     }
 
     // DOWN WALL
-    if (counter > 600) {
+    if (counter > 600) {    // After 30 seconds of gameplay
         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) {
+    if (counter > 1200) {   // After 60 seconds of gameplay
         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 ()
-{
+    // Flash screen if a wall is about to appear from a new direction
     if (counter == 170) {
         lcd.inverseMode();
     } else if (counter == 570) {
@@ -714,14 +313,15 @@
     } else {
         lcd.normalMode();
     }
+    lcd.refresh();
 }
 
-void game_timer_isr()   // sets flag for timer interrupt
+void gameTicker_isr()   // Sets flag for timer interrupt
 {
     g_timer_flag = 1;
 }
 
-void button_isr()
+void button_isr()   // Sets flag for button interrupt
 {
     g_button_flag = 1;
 }
@@ -729,27 +329,28 @@
 void initDisplay()   // initialises the LCD display
 {
     lcd.init();
+    wait(0.5);  // wait for LCD to initialise
     lcd.normalMode();
-    lcd.setBrightness(1.0F-backlight); // brightness pot on PCB is soldered in the wrong direction, (1.0F-backlight) inverts the reading
+    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
+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)
+    // 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
+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)
+    // 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
+    // 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
+    // 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) {
@@ -773,54 +374,40 @@
     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;
-        serial.printf("saves = %d \n",saves);
-    }
-}
-
-void initSerial()   // sets baud rate for serial
+void initSerial()   // Sets baud rate for serial
 {
     serial.baud(115200);
 }
-void initGame()
+void initGame()   // Initialises gameplay variables
 {
-
     g_button_flag = 0;
     leftwall.y = rand() % 27+8;
-    leftwall.x = 0;
+    leftwall.x = 82;
     rightwall.y = rand() % 27+8;
     rightwall.x = 6;
-    downwall.x = rand() % 58+13;
+    downwall.x = rand() % 52+19;
     downwall.y = 0;
-    upwall.x = rand() % 58+13;
+    upwall.x = rand() % 52+19;
     upwall.y = 0;
     rightwall.cooldown = 61;
     counter = 0;
     score = 0;
-
     saves = 5;
-
     i = 42;
     j = 24;
 
 }
 
-void blink()   // command for brief flash
+void flash()    // Produces a brief flash on screen
 {
     lcd.inverseMode();
     wait(0.2);
     lcd.normalMode();
 }
 
-void printHelp()
+void printHelp()    // Prints instructions
 {
     while (1) {
         lcd.printString("Try and",0,1);
@@ -829,9 +416,8 @@
         lcd.printString("possible!",0,4);
         lcd.refresh();
         if (g_button_flag) {
-            //g_button_flag = 0;
             playNote(NOTE_E2,0.1);
-            blink();
+            flash();
             lcd.clear();
             break;
         }
@@ -844,9 +430,8 @@
         lcd.printString("the joystick",0,4);
         lcd.refresh();
         if (g_button_flag) {
-            //g_button_flag = 0;
             playNote(NOTE_E2,0.1);
-            blink();
+            flash();
             lcd.clear();
             break;
         }
@@ -859,9 +444,8 @@
         lcd.printString("untouchable",0,4);
         lcd.refresh();
         if (g_button_flag) {
-            // g_button_flag = 0;
             playNote(NOTE_E2,0.1);
-            blink();
+            flash();
             lcd.clear();
             break;
         }
@@ -874,9 +458,8 @@
         lcd.printString("do it 5 times",0,4);
         lcd.refresh();
         if (g_button_flag) {
-            //  g_button_flag = 0;
             playNote(NOTE_E2,0.1);
-            blink();
+            flash();
             lcd.clear();
             break;
         }
@@ -889,9 +472,8 @@
         lcd.printString("the screen...",0,4);
         lcd.refresh();
         if (g_button_flag) {
-            //  g_button_flag = 0;
             playNote(NOTE_E2,0.1);
-            blink();
+            flash();
             lcd.clear();
             break;
         }
@@ -906,14 +488,14 @@
         if (g_button_flag) {
             g_button_flag = 0;
             playNote(NOTE_E2,0.1);
-            blink();
+            flash();
             lcd.clear();
             break;
         }
     }
 }
 
-void printScores()
+void printScores()   // Prints previous scores
 {
     while(1) {
         lcd.printString("Highscores",13,0);
@@ -936,14 +518,14 @@
         if (g_button_flag) {
             g_button_flag = 0;
             playNote(NOTE_E2,0.1);
-            blink();
+            flash();
             lcd.clear();
             break;
         }
     }
 }
 
-void draw_border()   // draws game border
+void draw_border()   // Draws game border
 {
     lcd.drawLine(7,2,81,2,1);
     lcd.drawLine(7,2,7,45,1);
@@ -955,10 +537,15 @@
     lcd.drawLine(83,0,83,47,1);
     lcd.drawLine(5,47,83,47,1);
 
+    lcd.drawRect(6,0,1,2,1);
+    lcd.drawRect(6,45,1,2,1);
+    lcd.drawRect(81,0,1,2,1);
+    lcd.drawRect(81,45,1,2,1);
+
     lcd.refresh();
 }
 
-void calculateHighscores()
+void calculateHighscores()   // Determines if player score is a new highscore
 {
     if (score > hiscore.one) {
         hiscore.three = hiscore.two;
@@ -972,22 +559,307 @@
     }
 }
 
-void initHiscores()
+void initHiscores()   // Initialises highscores
 {
     hiscore.one = 0;
     hiscore.two = 0;
     hiscore.three = 0;
 }
 
-void playNote(int freq, float time)
+void playNote(int freq, float time)   // Plays a tone of specific frequency and duration
 {
-    if(!MUTE) { // no sound if MUTE
-        buzzer.period(1.0/freq); // 1 KHz
+    if(backlight < 0.5F) { // Use potentiometer to switch sound on and off
+        buzzer.period(1.0/freq);
         buzzer.write(0.0);
         buzzer.write(0.5);
         wait(time);
         buzzer.write(0.0);
+    } else {    // If muted
+        wait(time); // Wait included so that function still takes the same time to execute
+    }
+}
+
+void countdown()    // Countdown for start of game
+{
+    wait(0.5);
+    lcd.printString("3",40,2);
+    playNote(NOTE_C4,0.2);
+    wait(0.3);
+    lcd.drawRect(10,10,64,28,2);
+    lcd.refresh();
+    wait(0.5);
+    lcd.printString("2",40,2);
+    playNote(NOTE_C4,0.2);
+    wait(0.3);
+    lcd.drawRect(10,10,64,28,2);
+    lcd.refresh();
+    wait(0.5);
+    lcd.printString("1",40,2);
+    playNote(NOTE_C4,0.2);
+    wait(0.3);
+    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);
+    playNote(NOTE_G4,0.5);
+    lcd.drawRect(10,10,64,28,2);
+    lcd.refresh();
+}
+
+void introScreen()   // Introduction animation
+{
+    wait (1.0);
+    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);
+    flash();
+}
+
+void proceed()   // Moves to selected menu option
+{
+    if (menu_item == 1) {
+        playNote(NOTE_B2,0.1);
+        initGame();
+        game_running = 1;
+    } else if (menu_item == 2) {
+        playNote(NOTE_B2,0.1);
+        initGame();
+        counter = 1200;         // Note - initGame must be called here as calling it after the menu loop will reset the counter, ergo no hard mode
+        game_running= 1;
+    } else if (menu_item == 3) {
+        playNote(NOTE_B2,0.1);
+        flash();
+        lcd.clear();
+        printHelp();
+    } else if (menu_item == 4) {
+        playNote(NOTE_B2,0.1);
+        flash();
+        lcd.clear();
+        printScores();
+    }
+}
+
+void setTickers(int speed)   // Dettaches tickers and reattaches them with specified speed
+{
+    gameTicker.detach();
+    gameTicker.attach(&gameTicker_isr,1.0/speed);
+    pollJoystick.detach();
+    pollJoystick.attach(&updateJoystick,1.0/speed);
+}
+
+void congratulate()    // Congratulates player if they got a highscore
+{
+    if (score >= hiscore.one) { // New highscore
+        wait(1.0);
+        lcd.printString("HIGHSCORE!",13,5);
+        // Highscore melody
+        playNote(NOTE_E3,0.1);
+        wait(0.1);
+        playNote(NOTE_C3,0.05);
+        wait(0.05);
+        playNote(NOTE_C3,0.05);
+        wait(0.05);
+        playNote(NOTE_C3,0.05);
+        wait(0.15);
+        playNote(NOTE_G3,0.1);
+        wait(0.1);
+        playNote(NOTE_E3,0.1);
+        wait(0.1);
+        playNote(NOTE_G3,0.1);
+        wait(0.1);
+        playNote(NOTE_C4,0.5);
+        wait(0.1);
+        lcd.printString("CONGRATS!!",13,5);
+        playNote(NOTE_E2,0.2);
+
+    } else if ((score >= hiscore.two) && (score < hiscore.one)) { // Second place
+        // Endgame melody
+        playNote(NOTE_A2,0.05);
+        wait(0.05);
+        playNote(NOTE_A2,0.05);
+        wait(0.05);
+        playNote(NOTE_A2,0.05);
+        wait(0.05);
+        playNote(NOTE_A2,0.3);
+
+    } else if ((score >= hiscore.three) && (score < hiscore.two)) { // Third place
+        // Endgame melody
+        playNote(NOTE_A2,0.05);
+        wait(0.05);
+        playNote(NOTE_A2,0.05);
+        wait(0.05);
+        playNote(NOTE_A2,0.05);
+        wait(0.05);
+        playNote(NOTE_A2,0.3);
+
+    } else { // No highscore
+        // Endgame melody
+        playNote(NOTE_A2,0.05);
+        wait(0.05);
+        playNote(NOTE_A2,0.05);
+        wait(0.05);
+        playNote(NOTE_A2,0.05);
+        wait(0.05);
+        playNote(NOTE_A2,0.3);
+    }
+}
+
+void menu()
+{
+    while(game_running == 0) {    // If game is not started
+        if (g_timer_flag) {
+            g_timer_flag = 0;
+
+            if ((joystick.direction == UP)||(joystick.direction == UPRIGHT)||(joystick.direction == UPLEFT)) {
+                menu_direction = 2;
+            } else if ((joystick.direction == DOWN)||(joystick.direction == DOWNRIGHT)||(joystick.direction == DOWNLEFT)) {
+                menu_direction = 1;
+            } else {
+                menu_direction = 0;
+            }
+
+            menu_item = fsm[state].output;                  // Which menu item the cursor is on
+            state = fsm[state].next_state[menu_direction];  // Moves up or down the menu, or stays on the same item, depending on joystick direction
+            lcd.clear();
+
+            lcd.printString("Start Easy",12,1);
+            lcd.printString("Start Hard",12,2);
+            lcd.printString("Help",12,3);
+            lcd.printString("Scores",12,4);
+
+            // Place cursor next to appropriate menu item
+            if (menu_item == 1) {
+                lcd.drawCircle(6,11,2,1);
+            } else if (menu_item == 2) {
+                lcd.drawCircle(6,19,2,1);
+            } else if (menu_item == 3) {
+                lcd.drawCircle(6,27,2,1);
+            } else {
+                lcd.drawCircle(6,35,2,1);
+            }
+            lcd.refresh();
+
+            if (((menu_direction == 1)&&(menu_item != 4))||((menu_direction == 2)&&(menu_item != 1))) {
+                playNote(NOTE_G2,0.05);    // Menu scrolling sound
+            }
+
+            if (g_button_flag) {    // If button press
+                g_button_flag = 0;
+                if (menu_item == 1) {
+                    playNote(NOTE_B2,0.1);
+                    initGame();
+                    game_running = 1;
+                } else if (menu_item == 2) {
+                    playNote(NOTE_B2,0.1);
+                    initGame();
+                    counter = 1200;         // Note - initGame must be called here as calling it after the menu loop will reset the counter, ergo no hard mode
+                    game_running= 1;
+                } else if (menu_item == 3) {
+                    playNote(NOTE_B2,0.1);
+                    flash();
+                    lcd.clear();
+                    printHelp();
+                } else if (menu_item == 4) {
+                    playNote(NOTE_B2,0.1);
+                    flash();
+                    lcd.clear();
+                    printScores();
+                }          
+            }
+        }
+    }
+
+    // When one of the two gameplay options are selected, the loop is broken and moves onto countdown/gameplay
+    flash();
+    lcd.clear();
+    draw_border();          // Draw game border
+    countdown();            // Countdown to game start
+}
+
+void playGame()
+{
+    while (game_running == 1) {   // Gameplay loop
+
+        if ( g_timer_flag ) {       // Ticker interrupt
+            g_timer_flag = 0;       // Clear flag
+            moveWall();             // Move wall obstacles across screen
+            moveBall();             // Move player
+            invincible();           // Check if invincibility has been enabled
+            checkBorderCollision(); // Check if player has hit a border
+            checkWallCollision();   // Check if player has hit a wall
+            updateScreen();         // Redraw screen with new object positions
+
+            counter++;              // Increment counter each cycle (approx. 20 points a second)
+            score++;                // This is seperate from counter for the purposes of keeping score 0 when Hard Start is selected to allow 4 walls from the start
+
+            // Increase wall cooldown variable
+            leftwall.cooldown++;
+            rightwall.cooldown++;
+            downwall.cooldown++;
+            upwall.cooldown++;
+        }
+        sleep();    // Put processor to sleep till next interrupt
+    }
+
+    // Briefly freezes screen and plays game-over melody if player hits wall
+    flash();
+    playNote(NOTE_E4,0.1);
+    playNote(NOTE_C4,0.1);
+    playNote(NOTE_A3,0.1);
+    playNote(NOTE_A2,0.3);
+    wait(0.9);
+    lcd.clear();
+    wait(0.2);
+}
+
+void resultsScreen()
+{
+    calculateHighscores();  // Determines if player has a new highscore
+    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 {
-        wait(time); // wait included so that function still takes the same time to execute
+        lcd.printString ("Wow!",54,2);  // if score is too large to fit in box, print text instead (would require 8.3 minutes of gameplay...)
+        lcd.printString ("Wow!",54,3);
+    }
+
+    lcd.refresh();
+    congratulate(); // Congratulates the player if they have a new highscore
+    lcd.refresh();
+    while(1) {  // Remain on screen until button is pressed
+        if (g_button_flag) {
+            g_button_flag = 0;
+            playNote(NOTE_E2,0.1);
+            flash();
+            lcd.clear();
+            break;
+        }
     }
 }
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.h	Thu May 05 14:52:59 2016 +0000
@@ -0,0 +1,393 @@
+/**
+@file main.h
+@brief Header file containing function prototypes, defines and global variables
+@author Max Hamilton
+@date May 2015
+*/
+#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
+
+/** 
+@namespace lcd
+@brief Creates N5110 object for Nokia LCD screen
+@brief Pin connections are VCC, SCE, RST, D/C, MOSI, SCLK, LED
+*/
+N5110 lcd (PTD3 , PTA0 , PTC4 , PTD0 , PTD2 , PTD1 , PTC3); // VCC, SCE, RST, D/C, MOSI, SCLK, LED
+
+/** 
+@namespace xPot
+@brief AnalogIn to control the joystick's x-position
+*/
+AnalogIn xPot(PTB11);
+
+/** 
+@namespace yPot
+@brief AnalogIn to control the joysticks's y-position
+*/
+AnalogIn yPot(PTB10);
+
+/** 
+@namespace button
+@brief InterruptIn for joystick button
+*/
+InterruptIn button (PTB3);
+
+/** 
+@namespace buzzer
+@brief PwmOut to control buzzer
+*/
+PwmOut buzzer(PTA2);
+
+/** 
+@namespace backlight
+@brief AnalogIn object for 10k potentiometer to control screen brightness
+*/
+AnalogIn backlight(PTB2);
+
+/** 
+@namespace pollJoystick
+@brief Ticker object to read the joystick position at regular intervals
+*/
+Ticker pollJoystick;
+
+/** 
+@namespace gameTicker
+@brief Ticker object to regularly call game functions
+*/
+Ticker gameTicker;
+
+/**
+@namespace serial
+@brief USB serial connection for debugging
+*/
+Serial serial(USBTX,USBRX);
+
+
+///Create enumerated type (0,1,2,3 etc. for direction)
+enum DirectionName {
+    UP,
+    DOWN,
+    LEFT,
+    RIGHT,
+    UPRIGHT,
+    UPLEFT,
+    DOWNRIGHT,
+    DOWNLEFT,
+    CENTRE,
+    UNKNOWN
+};
+
+typedef struct JoyStick Joystick;
+/**
+@struct Joystick
+@brief Struct for joystick parameters/inputs
+@param x - current x value
+@param x0 - calibrated centre x value
+@param y - current y value
+@param y0 - calibrated centre y value
+@param direction - direction joystick is set to
+@see {DirectionName}
+*/
+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
+};
+
+// creates 'joystick' instance of struct
+Joystick joystick;
+
+typedef struct Wall Wall;
+/**
+@struct Wall
+@brief Struct for storing properties of moving walls
+@param x - Current x value of wall (relative to the centre of wall gap)
+@param y - Current y value of wall (relative to the centre of wall gap)
+@param random - Randomly generated integer to determine when the wall starts moving
+@param cooldown - Variable to stop wall reappearing directly after if disappears
+@param moveFlag - flag to determine if wall is moving
+@param genFlag - flag to determine if wall is generated
+*/
+struct Wall {
+    int x;                    
+    int y;                  
+    int random;              
+    int cooldown;              
+    volatile bool moveFlag;    
+    volatile bool genFlag;     
+};
+
+// Creates instances of structs
+Wall leftwall;              // left-moving wall
+Wall rightwall;             // right-moving wall
+Wall downwall;              // down-moving wall
+Wall upwall;                // up-moving wall
+
+typedef const struct State STyp;
+/**
+@struct STyp
+@brief Struct for game menu parameters
+@param output - output of the menu state
+@param next_state - array containing next state depending on joystick direction
+*/
+
+// Creates instance of struct
+struct State {
+    int output;                 
+    int next_state[3];          
+};
+
+// array containing menu state outputs and next state possibilites
+STyp fsm[4] = { 
+    {1,{0,1,0}},
+    {2,{1,2,0}},
+    {3,{2,3,1}},
+    {4,{3,3,2}}
+};
+
+typedef struct Highscore Highscore;
+/**
+@struct State
+@brief Struct player highscores
+@param one - Highest player score
+@param two - Second highest player score
+@param three - Three highest player score
+*/
+struct Highscore {
+    int one;                    
+    int two;               
+    int three;             
+};
+
+// Creates instance of struct
+Highscore hiscore;
+
+// - - - - - - - - - - - - - - - - - - - - - - - - Variables  - - - - - - - - - - - - - - - - - - - - - - - - 
+
+/**
+@brief Player x-coordinate
+*/
+int i;                     
+
+/**
+@brief Player y-coordinate
+*/
+int j;
+
+/**
+@brief Int to keep track of how many time gameplay ticker funtion has been called
+*/                     
+int counter = 0;
+
+/**
+@brief Int to keep track of player score
+*/                  
+int score = 0;
+
+/**
+@brief how many uses of invincibility the player has left
+*/  
+int saves = 5;
+
+/**
+@brief Int to count how long the player stays invincible.
+@brief Increases with counter variable, and resets when invincibility runs out
+*/ 
+int saves_cool = 0;
+
+/**
+@brief Flag to determine if player is invincible or not.
+*/ 
+volatile bool mortal = 1;
+
+/**
+@brief Flag for gameTicker isr.
+*/ 
+volatile int g_timer_flag = 0; 
+
+/**
+@brief Flag to determine if game is running
+*/ 
+volatile int game_running = 0;      
+
+/**
+@brief Flag for button isr.
+*/ 
+volatile int g_button_flag = 0;
+
+/**
+@brief Int to determine what menu item to go to depending on joystick direction
+*/ 
+int menu_direction;
+
+/**
+@brief Output of the menu state the player has selected
+*/ 
+int state;
+/**
+@brief Int to determine which menu state the player is on
+*/ 
+int menu_item;
+
+// - - - - - - - - - - - - - - - - - - - - - - - - Function Prototypes - - - - - - - - - - - - - - - - - - - - - - - - 
+
+/** 
+@brief Calibrates josytick and gets centred value
+*/
+void calibrateJoystick();
+
+/** 
+@brief Reads direction joystick is moved in
+*/
+void updateJoystick();
+
+/** 
+@brief Initialise display
+*/
+void initDisplay();
+
+/** 
+@brief Initialises the gameplay variables for the start of a new game
+*/
+void initGame();
+
+/** 
+@brief Initialises serial and sets baud rate for data transfer
+*/
+void initSerial();
+
+/** 
+@brief Initialises highscores
+*/
+void initHiscores();
+
+/** 
+@brief Interrupt for game loop, triggered by gameTicker
+*/
+void gameTicker_isr();
+
+/** 
+@brief Button interrupt, triggered by joystick button
+*/
+void button_isr();
+
+/** 
+@brief Reads direction of the joystick and moves the player accordingly
+*/
+void moveBall();
+
+/** 
+@brief Moves the walls across the game screen
+*/
+void moveWall();
+
+/** 
+@brief Ignores wall collisions if player has triggered invincibility.
+@brief Can only be used 5 times.
+@brief Disables the 'checkWallCollisions()' function as long as the player is invincible.
+*/
+void invincible();
+
+/** 
+@brief Checks if the player has hit one of the walls.
+@brief Sets the 'game_running' = 0 to end the game if collision occurs.
+@brief Bypassed if 'mortal' variable is false.
+*/
+void checkWallCollision();
+
+/** 
+@brief Checks if the player has hit the game border.
+@brief Stops the player moving off the screen.
+*/
+void checkBorderCollision();
+
+/** 
+@brief Redraws the screen with the new positions of the player and walls.
+@brief Also redraws borders and any remianing invincibility indicators.
+*/
+void updateScreen();
+
+/**
+@brief Prints game instructions when Help is selected in the menu.
+@brief Move through instructions by pressing the joystick.
+*/
+void printHelp();
+
+/** 
+@brief Shows the top three highscores when Scores is selected in the menu.
+*/
+void printScores();
+
+/**
+@brief Sets new highscore if one has been achieved
+@brief If the latest score is set as a highscore, previous highscores are shifted down accordingly.
+*/
+void calculateHighscores();
+
+/** 
+@brief Makes the screen flash briefly.
+@brief IMPORTANT: Do not put in the ISR as it contains a 'wait()' function.
+*/
+void flash();
+
+/** 
+@brief Plays a note of specific frequency and duration in seconds.
+@brief If the preproccesor directive MUTE is defined as 1, the code instead waits the defined time.
+@brief IMPORTANT: Do not put in the ISR as it contains a 'wait()' function.
+@param freq - The note frequency (N.B. program uses definitions from the 'tone.h' file).
+@param time - The time the note plays for in seconds.
+*/
+void playNote(int freq, float time);    
+
+/**
+@brief Draws the game border.
+*/
+void draw_border();                     
+
+/**
+@brief Runs a countdown before the start of the game.
+*/
+void countdown();
+
+/**
+@brief Displays the opening title and animation for the game.
+*/
+void introScreen();
+
+/** 
+@brief Moves to selected part of the program from menu (Easy Start, Hard Start, Help, Scores).
+*/
+void proceed();
+
+/** 
+@brief Dettaches tickers from current speed and reattaches them with a new speed
+@param speed - speed to re-attach tickers with (rate per second = 1/speed)
+*/                 
+void setTickers(int time);  
+
+/** 
+@brief Congratulate the player if they have got a new highscore
+*/
+void congratulate();    
+
+/** 
+@brief Creates and navigates the game menu
+*/
+void menu();
+
+/**
+@brief Starts the gameplay
+*/
+void playGame();
+
+/**
+@brief Displays player score and current highscore after game ends
+*/
+void resultsScreen();