“Race Collision” is a one player game in which a truck has to avoid “particles” that appear on the road. By the use of the joystick, the player can guide themselves through the menu system to start the game. The truck is the main element of the game and it can be moved from side to side with the joystick. The road curves randomly from time to time and the player has to be careful to keep the truck within the road boundaries. Particles appear on the screen at random positions and 4 collisions lead to the end of the game.

Dependencies:   ELEC2645_JoystickLCD_LPC1768_2021

Files at this revision

API Documentation at this revision

Comitter:
alex_20
Date:
Thu May 06 12:04:45 2021 +0000
Parent:
8:1fc5e14b0db6
Commit message:
Race Collision for ELEC2645

Changed in this revision

lib/Ball.cpp Show annotated file Show diff for this revision Revisions of this file
lib/Ball.h Show annotated file Show diff for this revision Revisions of this file
lib/Car.cpp Show annotated file Show diff for this revision Revisions of this file
lib/Car.h Show annotated file Show diff for this revision Revisions of this file
lib/GameEngine.cpp Show annotated file Show diff for this revision Revisions of this file
lib/GameEngine.h Show annotated file Show diff for this revision Revisions of this file
lib/Road.cpp Show annotated file Show diff for this revision Revisions of this file
lib/Road.h Show annotated file Show diff for this revision Revisions of this file
lib/ShiftReg.cpp Show annotated file Show diff for this revision Revisions of this file
lib/ShiftReg.h Show annotated file Show diff for this revision Revisions of this file
lib/Utils.cpp Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
--- a/lib/Ball.cpp	Sat Apr 24 21:31:19 2021 +0000
+++ b/lib/Ball.cpp	Thu May 06 12:04:45 2021 +0000
@@ -3,16 +3,15 @@
 // constructure
 Ball::Ball() {}
 
-void Ball::init() {    
-    //initialize parameters
-    //say that trajectory is 0
+void Ball::init(int radius) {    
+    if (radius > 4 | radius < 0){
+        radius = 4;
+    } 
 }
 
-//std::vector<Vector2Df> Ball::get_path() {
-//    path points = utils.getCurve;
-//    return path_points; 
-//    }
-
-void Ball::draw(N5110 &lcd, std::vector<Vector2Df> path_points, int radius, float iterator) { 
-    lcd.drawCircle(static_cast<int>(path_points[iterator].x), static_cast<int>(path_points[iterator].y), radius, FILL_TRANSPARENT);
+void Ball::draw(N5110 &lcd, std::vector<Vector2Df> path_points, int radius, int iterator) { 
+    
+    if(iterator > 0) {
+        lcd.drawCircle(static_cast<int>(path_points[iterator].x), static_cast<int>(path_points[iterator].y), radius, FILL_TRANSPARENT);
+    }
 }
\ No newline at end of file
--- a/lib/Ball.h	Sat Apr 24 21:31:19 2021 +0000
+++ b/lib/Ball.h	Thu May 06 12:04:45 2021 +0000
@@ -12,9 +12,9 @@
 public:
 
     Ball();
-    void init();
+    void init(int radius);
 //  std::vector<Vector2Df> get_path;
-    void draw(N5110 &lcd, std::vector<Vector2Df> path_points, int radius, float iterator);
+    void draw(N5110 &lcd, std::vector<Vector2Df> path_points, int radius, int iterator);
 
 };
 #endif 
--- a/lib/Car.cpp	Sat Apr 24 21:31:19 2021 +0000
+++ b/lib/Car.cpp	Thu May 06 12:04:45 2021 +0000
@@ -3,10 +3,14 @@
 // constructure
 Car::Car() {}
 
-void Car::init() {    
-    //initialize parameters
-    int direction = STRAIGHT;
+void Car::init(int direction, int magnitude, int start_pos) {    
+    // initialize parameters
+    // direction = STRAIGHT;
+    // magnitude = 0; 
+    // start_pos = 38
     _direction = direction;
+    _magnitude = magnitude;
+    _start_pos = start_pos;
 }
 
 
@@ -21,29 +25,27 @@
     else {
         _direction = STRAIGHT;
     } 
-     
     return _direction;
 }
 
     
 int Car::get_magnitude(float coord_x) {
-    int magnitude;
     
     if(coord_x > -0.2 && coord_x < 0.2) {
-        magnitude = 0;
+        _magnitude = 0;
     } 
-    else if(coord_x > 0.8 && coord_x < -0.8) {
-        magnitude = 2;
+    else if(coord_x > 0.8 || coord_x < -0.8) {
+        _magnitude = 2;
     }    
     else {
-        magnitude = 1;
+        _magnitude = 1;
     } 
-    return magnitude;
+    return _magnitude;
 }
 
 
-void Car::draw(N5110 &lcd, float start_pos, float start_angle) { 
-    int x = static_cast<int>(start_pos);
+void Car::draw(N5110 &lcd, int start_pos, float start_angle) { 
+    int x = start_pos;
     int y = static_cast<int>(start_angle);
     
     const int rear_face[12][10] = {
@@ -132,13 +134,13 @@
             lcd.drawSprite(x + y - 2,36,6,4,(int *)wheel);      // wheel 3
             lcd.drawSprite(x - 2,40,6,4,(int *)wheel);          // wheel 1 
             
-            lcd.drawLine(x,42,x + y,38,0,1);                    // line to connect corner 3
-            lcd.drawLine(x + 9,42,x + y + 9,38,0,1);            // line to connect corner 4
+            lcd.drawLine(x,42,x + y,38,0,1);                    // line to connect corner left bottom
+            lcd.drawLine(x + 9,42,x + y + 9,38,0,1);            // line to connect corner right bottom
             
             lcd.drawSprite(x + y + 8,36,6,4,(int *)wheel);      // wheel 4
             
-            lcd.drawLine(x,32,x + y,26,0,1);                    // line to connect corner 1
-            lcd.drawLine(x + 9,32,x + y + 9,26,0,1);            // line to connect corner 2
+            lcd.drawLine(x,32,x + y,26,0,1);                    // line to connect corner left up
+            lcd.drawLine(x + 9,32,x + y + 9,26,0,1);            // line to connect corner right up
             
             lcd.drawSprite(x,32,12,10,(int *)rear_face);
             lcd.drawSprite(x + 8,40,6,4,(int *)wheel);          // wheel 2
@@ -147,19 +149,19 @@
             
         case LEFT:
             lcd.drawSprite(x - y - 2,26,10,10,(int *)window);
-            //lcd.drawSprite(x - y,26,12,10,(int *)front_face);
             lcd.drawLine(x - y,26,x - y + 9,26,0,1);            // front face  
             lcd.drawLine(x - y,26,x - y,36,0,1);                // front face 
             
             lcd.drawSprite(x - y + 8,36,6,4,(int *)wheel);      // wheel 4
             lcd.drawSprite(x + 8,40,6,4,(int *)wheel);          // wheel 2
             
-            lcd.drawLine(x + 9,42,x - y + 9,38,0,1);            // line to connect corner 4
+            lcd.drawLine(x + 9,42,x - y + 9,38,0,1);            // line to connect corner right bottom
+            lcd.drawLine(x,42,x - y,38,0,1);                    // line to connect corner left bottom
             
             lcd.drawSprite(x - 2 - y,36,6,4,(int *)wheel);      // wheel 3
             
-            lcd.drawLine(x,32,x - y,26,0,1);                    // line to connect corner 1
-            lcd.drawLine(x + 9,32,x - y + 9,26,0,1);            // line to connect corner 2
+            lcd.drawLine(x,32,x - y,26,0,1);                    // line to connect corner right up
+            lcd.drawLine(x + 9,32,x - y + 9,26,0,1);            // line to connect corner left up
             
             lcd.drawSprite(x,32,12,10,(int *)rear_face);
             lcd.drawSprite(x - 2,40,6,4,(int *)wheel);          // wheel 1
--- a/lib/Car.h	Sat Apr 24 21:31:19 2021 +0000
+++ b/lib/Car.h	Thu May 06 12:04:45 2021 +0000
@@ -17,13 +17,15 @@
 public:
 
     Car();
-    void init();
+    void init(int direction, int magnitude, int start_pos);
     int get_direction(float coord_x);
     int get_magnitude(float coord_x);
-    void draw(N5110 &lcd, float start_pos, float start_angle);
+    void draw(N5110 &lcd, int start_pos, float start_angle);
     
 private:
 
     int _direction;
+    int _magnitude;
+    int _start_pos;
 };
 #endif 
\ No newline at end of file
--- a/lib/GameEngine.cpp	Sat Apr 24 21:31:19 2021 +0000
+++ b/lib/GameEngine.cpp	Thu May 06 12:04:45 2021 +0000
@@ -1,89 +1,131 @@
-#include "PongEngine.h"
-
-PongEngine::PongEngine(){ _lives = 4; }    
+#include "GameEngine.h"
 
-void PongEngine::init(int paddle_position, int paddle_height, int paddle_width, int ball_size, int speed){
-    //printf("Pong Engine: Init\n");
-    _ball.init(ball_size,speed);
-    _paddle.init(paddle_position, paddle_height, paddle_width);
-}
+// constructure
+GameEngine::GameEngine() {}    
 
-int PongEngine::update(UserInput input) {   
-    //printf("Pong Engine: Update\n");
-    check_goal();  // checking for a goal is a priority 
-    _ball.update();
-    _paddle.update(input);
-    // important to update paddles and ball before checking collisions so can
-    // correct for it before updating the display
-    check_wall_collision();
-    check_paddle_collision();
+void GameEngine::init(int road_direction, 
+                      int road_inclination, 
+                      int radius, 
+                      std::vector<Vector2Df> ball_path,
+                      int car_direction, 
+                      int car_magnitude, 
+                      int car_start_pos, 
+                      float road_offset, 
+                      float car_turn, 
+                      float ball_it)
+{    
+    _road_direction = road_direction;
+    _road_inclination = road_inclination;
+    _radius = radius; 
+    _ball_path = ball_path;
+    _car_direction = car_direction;
+    _car_magnitude = car_magnitude;
+    _car_start_pos = car_start_pos;
+    _road_offset = road_offset; 
+    _car_turn = car_turn;
+    _ball_it = ball_it;
     
-    return _lives;
-}
-
-void PongEngine::draw(N5110 &lcd) {
-    _road.draw(lcd);
+    // initialize all the parameters from the game  
+    _ball.init(_radius);
+    _road.init(_road_direction, _road_inclination);
+    _car.init(_car_direction, _car_magnitude, _car_start_pos);
 }
 
-void PongEngine::check_wall_collision() {
-    //printf("Pong Engine: Check Wall Collision\n");
-    // read current ball attributes
-    Position2D ball_pos = _ball.get_pos();
-    Position2D ball_velocity = _ball.get_velocity();
-    int size = _ball.get_size();
+
+void GameEngine::update(float car_coord) {  
 
-    // check if hit top wall
-    if (ball_pos.y <= 1) {  //  1 due to 1 pixel boundary
-        ball_pos.y = 1;  // bounce off ceiling without going off screen
-        ball_velocity.y = -ball_velocity.y;  // flip velocity
-    } else if (ball_pos.y + size >= (HEIGHT-1) ) {
-        // hit bottom
-        ball_pos.y = (HEIGHT-1) - size;  // stops ball going off screen
-        ball_velocity.y = -ball_velocity.y;    // flip velcoity 
-    } else if (ball_pos.x + size >= (WIDTH-1) ) {
-        // hit right wall
-        ball_pos.x = (WIDTH-1) - size;  // stops ball going off screen
-        ball_velocity.x = -ball_velocity.x;    // flip velcoity 
+    // Get the user input to know where to draw the car
+    _car_direction = _car.get_direction(car_coord); 
+    _car_magnitude = _car.get_magnitude(car_coord); 
+    
+    if(_car_direction == 0) {       // STRAIGHT
+        _car_turn = 0;
+    }  
+    if(_car_direction == 2) {       // LEFT
+        if(_car_magnitude == 1) {
+            _car_turn = 4.0;
+        }
+        if(_car_magnitude == 2) {   
+            _car_turn = 6.0;
+        }
+        _car_start_pos -=  0.2 * _car_magnitude; 
+    }          
+    if(_car_direction ==  1) {      // RIGHT
+        if(_car_magnitude == 1) {
+            _car_turn = 4.0;
+        }
+        if(_car_magnitude == 2) {
+            _car_turn = 6.0;
+        }
+        _car_start_pos += 0.2 * _car_magnitude;
+    }
+    
+    // Define boundaries for car movement
+    if(_car_start_pos < 22.0) {
+        _car_start_pos = 22.0;
+    }   
+    if(_car_start_pos > 55.0) {
+        _car_start_pos = 55.0;
     } 
 
-    // update ball parameters
-    _ball.set_velocity(ball_velocity);
-    _ball.set_pos(ball_pos);
+    // Make sure that middle line on road is moving continuously 
+   _road_offset += 0.01;
+   
+   
+    if(_ball_it < _ball_path.size()){
+        _ball_it += 0.3;
+        // Get the ball generation function 
+        _iterator = static_cast<int>(_ball_path.size() - _ball_it); 
+    }
+    else {
+        _iterator = 0;
+    }
 }
 
-void PongEngine::check_paddle_collision() {
-    //printf("Pong Engine: Check Paddle Collision\n");
-    // read current ball and paddle attributes
-    Position2D ball_pos = _ball.get_pos();
-    Position2D ball_velocity = _ball.get_velocity();
-    Position2D paddle_pos = _paddle.get_pos();  // paddle
+void GameEngine::reset_ball() {
+    _ball_it = 0;    
+}
+
 
-    // see if ball has hit the paddle by checking for overlaps
-    if (
-        (ball_pos.y >= paddle_pos.y) && //top
-        (ball_pos.y <= paddle_pos.y + _paddle.get_height() ) && //bottom
-        (ball_pos.x >= paddle_pos.x) && //left
-        (ball_pos.x <= paddle_pos.x + _paddle.get_width() )  //right
-    ) {
-        // if it has, fix position and reflect x velocity
-        ball_pos.x = paddle_pos.x + _paddle.get_width();
-        ball_velocity.x = -ball_velocity.x;
+void GameEngine::draw(N5110 &lcd, Utils &utils) {
+    _road.draw(lcd, utils, _road_direction, _road_offset);
+    _ball.draw(lcd, _ball_path, _radius, _iterator);
+    _car.draw(lcd, _car_start_pos, _car_turn);
+}
+
+
+int GameEngine::check_collision() {
+    if (static_cast<int>(_ball_path[_iterator].y) > 38
+        && static_cast<int>(_ball_path[_iterator].y) < 46
+        && static_cast<int>(_ball_path[_iterator].x) <= _car_start_pos + 12
+        && static_cast<int>(_ball_path[_iterator].x) >= _car_start_pos - 2)
+    {
+        return 1;   
     }
 
-    // write new attributes
-    _ball.set_velocity(ball_velocity);
-    _ball.set_pos(ball_pos);
+    
+    else {
+        return 0;   
+    }
+} 
+   
+    
+void GameEngine::generate_road(float tilt) {  
+
+    if (_road_direction < tilt - 0.05) {
+        _road_direction += 0.05;
+    }
+        
+    else if (_road_direction > tilt + 0.05) {
+        _road_direction -= 0.05;
+    }    
+    
+    else {
+        _road_direction = tilt;
+    }
 }
 
-void PongEngine::check_goal() {
-    //printf("Pong Engine: Check Goal\n");
-    Position2D ball_pos = _ball.get_pos();
-    int size = _ball.get_size();
-    int speed = abs(_ball.get_velocity().x);  // speed is magnitude of velocity
-    // check if ball position has gone off the left
-    if (ball_pos.x + size < 0) {
-        // reset the ball
-        _ball.init(size,speed);
-        _lives--;  // lose a life
-    }   
+void GameEngine::generate_ball(int start){
+    std::vector<Vector2Df> ball_path = _utils.getCurve(42 + start,48,42 + 5 * _road_direction + start, 30 - abs(_road_direction),42 + start,12);
+    _ball_path = ball_path;
 }
\ No newline at end of file
--- a/lib/GameEngine.h	Sat Apr 24 21:31:19 2021 +0000
+++ b/lib/GameEngine.h	Thu May 06 12:04:45 2021 +0000
@@ -7,21 +7,64 @@
 #include "Utils.h"
 #include "Road.h"
 #include "Car.h"
-#include "Vector.h"
+#include "Vector.h"    
 
-class GameEngine {
-    public:
-        GameEngine();  // pass in the lcd object from the main file
-        void init(int paddle_position,int paddle_height,int paddle_width,int ball_size,int speed);
-        int update(UserInput input);
-        void sign(N5110 &lcd, Road &road);
-        void draw(N5110 &lcd);
-    private:
-        void check_collision();
-        void generate_ball();
-        void generate_road();
-        Ball _ball;
-        Road _road;
+class GameEngine 
+{
+public:
+    
+    GameEngine();     
+        
+    /** Initialize game
+    *   @param  road_direction     - set up the road as STRAIGHT (0)
+    *   @param  road_inclination   - set up the inclination of the road as 0
+    *   @param  radius             - set the ball radius as 4
+    *   @param  ball_path          - initial the ball start position
+    *   @param  car_direction      - set up the car view as STRAIGHT (0)
+    *   @param  car_magnitude      - set up the car tilt as 0
+    *   @param  car_start_pos      - set up the car position as 38 (right in the centre)
+    *   @param  road_offset        - should increase continuously 
+    *   @param  car_turn           - the angle at which the car turns
+    *   @param  ball_it            - the iterator that draws the ball
+    **/
+    void init(int road_direction, 
+              int road_inclination, 
+              int radius, 
+              std::vector<Vector2Df> ball_path,
+              int car_direction, 
+              int car_magnitude, 
+              int car_start_pos, 
+              float road_offset, 
+              float car_turn, 
+              float ball_it);
+                  
+    int check_collision();   
+    void update(float car_coord);
+    void sign(N5110 &lcd, Road &road);
+    void reset_ball();
+    void draw(N5110 &lcd, Utils &utils);
+    void generate_road(float tilt);  
+    void generate_ball(int start);  
+    
+    
+private:
+
+    Ball _ball;
+    Road _road;
+    Car _car;
+    Utils _utils;
+    
+    float _road_offset;                     // Road
+    float _road_direction;                  // Road, i
+    int _road_inclination;                  // Road
+    float _ball_it;                         // Ball, it
+    int _radius;                            // Ball
+    std::vector<Vector2Df> _ball_path;      // Ball
+    int _iterator;                        // Ball
+    int _car_direction;                     // Car
+    int _car_magnitude;                     // Car
+    float _car_start_pos;                   // Car
+    float _car_turn;                        // Car, y
+    
 };
-
 #endif
\ No newline at end of file
--- a/lib/Road.cpp	Sat Apr 24 21:31:19 2021 +0000
+++ b/lib/Road.cpp	Thu May 06 12:04:45 2021 +0000
@@ -3,24 +3,23 @@
 // constructure
 Road::Road() {}
 
-void Road::init() {    
+void Road::init(float direction, float inclination) {    
     //initialize parameters
-    int direction = 0;
-    int inclination = 0;
-    
     _direction = direction;
     _inclination = inclination;
 }
 
-void Road::set_inclination(int inclination) {
+void Road::set_inclination(float inclination) {
     _inclination = inclination; 
     }
 
 void Road::draw(N5110 &lcd, Utils &utils, float direction, float offset) { 
+    // direction is i in the main code
+    // use "+= or -= speed" to get the direction of the road
     lcd.drawLine (0,12,84,12,0,1);
-    std::vector<Vector2Df> curve_points_1 = utils.getCurve(12,48,20 + 5 * direction, 30 - abs(direction),28,12);
-    std::vector<Vector2Df> curve_points_2 = utils.getCurve(72,48,64 + 5 * direction, 30 - abs(direction),56,12);
-    std::vector<Vector2Df> curve_points_3 = utils.getCurve(42,48,42 + 5 * direction, 30 - abs(direction),42,12);
+    std::vector<Vector2Df> curve_points_1 = utils.getCurve(12,48,20 + 5 * direction, 30 - abs(direction),28,12);    // LEFT
+    std::vector<Vector2Df> curve_points_2 = utils.getCurve(72,48,64 + 5 * direction, 30 - abs(direction),56,12);    // RIGHT
+    std::vector<Vector2Df> curve_points_3 = utils.getCurve(42,48,42 + 5 * direction, 30 - abs(direction),42,12);    // MIDDLE
         
     lcd.drawCurve(curve_points_1, 0, 1, TYPE_SOLID);
     lcd.drawCurve(curve_points_2, 0, 1, TYPE_SOLID);
--- a/lib/Road.h	Sat Apr 24 21:31:19 2021 +0000
+++ b/lib/Road.h	Thu May 06 12:04:45 2021 +0000
@@ -13,15 +13,15 @@
 public:
 
     Road();
-    void init();
-    void set_inclination(int inclination);
+    void init(float direction, float inclination);
+    void set_inclination(float inclination);
     void draw(N5110 &lcd, Utils &utils, float direction, float offset);
     void draw_warn(N5110 &lcd, int warn_position);
 
 private:
 
-    int _inclination;
-    int _direction;
+    float _inclination;
+    float _direction;
 
 };
 #endif 
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/ShiftReg.cpp	Thu May 06 12:04:45 2021 +0000
@@ -0,0 +1,26 @@
+#include "ShiftReg.h"
+
+ShiftReg::ShiftReg()
+{
+    clkout = new DigitalOut(p7);
+    dataout = new DigitalOut(p5);
+    latchout = new DigitalOut(p30);
+}
+
+ShiftReg::~ShiftReg()
+{
+    delete clkout;
+    delete dataout;
+    delete latchout;
+}
+
+void ShiftReg::write(int data)
+{
+    *latchout = 0;
+    for (int i = 7; i >=  0; i--) {
+        *clkout = 0;
+        *dataout = (data & (1 << i)) != 0;
+        *clkout = 1;
+    }
+    *latchout = 1;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/ShiftReg.h	Thu May 06 12:04:45 2021 +0000
@@ -0,0 +1,23 @@
+#ifndef SHIFTREG_H
+#define SHIFTREG_H
+
+#include <mbed.h>
+
+/** A simple serial driver for a shift register that uses only three digital out pins.
+* Based on a fork of Ollie8/ShiftOut
+*/
+class ShiftReg {
+
+    public :
+    
+        ShiftReg();    
+        ~ShiftReg();
+        void write(int data);
+         
+    private :
+        DigitalOut *clkout;
+        DigitalOut *dataout;
+        DigitalOut *latchout;
+};
+
+#endif
\ No newline at end of file
--- a/lib/Utils.cpp	Sat Apr 24 21:31:19 2021 +0000
+++ b/lib/Utils.cpp	Thu May 06 12:04:45 2021 +0000
@@ -25,7 +25,7 @@
 {  
     // vertor that will store all points 
     std::vector<Vector2Df> curve_points;
-    for(float i = 0 ; i <= 1 ; i += 0.2)
+    for(float i = 0 ; i <= 1 ; i += 0.05)
     {
         float xa, ya, xb, yb; 
         xa = curveEquation(x0 , x1 , i);
--- a/main.cpp	Sat Apr 24 21:31:19 2021 +0000
+++ b/main.cpp	Thu May 06 12:04:45 2021 +0000
@@ -4,6 +4,7 @@
  */
 
 #include "mbed.h"
+#include "ShiftReg.h" 
 #include "Joystick.h"
 #include "N5110.h"
 #include "Road.h"
@@ -11,13 +12,20 @@
 #include "Utils.h"
 #include "Vector.h"
 #include "Ball.h"
+#include "Vector.h"
+#include "GameEngine.h"
+#include <stdlib.h>     
+#include <time.h>       
 
 // objects
 // BusOut leds(LED4,LED3,LED2,LED1);
 
 N5110 lcd(p14,p8,p9,p10,p11,p13,p21); 
-DigitalIn button_A(p29);
+InterruptIn button_A(p29);
+DigitalIn button_B(p28);
 DigitalIn button_C(p27);
+DigitalIn button_D(p26);
+ShiftReg seven_seg;
 
 //                 y   x  
 Joystick joystick(p20,p19);
@@ -30,6 +38,13 @@
 Utils utils;
 Car car;
 Ball ball;
+GameEngine game;
+Ticker ticker;
+
+// flag - must be volatile as changes within ISR
+// g_ prefix makes it easier to distinguish it as global
+volatile int g_timer_flag = 0;
+volatile int g_buttonA_flag = 0;
 
 #define speed 0.01
 #define road_speed 0.016
@@ -37,93 +52,158 @@
 
 // functions
 void init_buttons();
+void init();
+void render();
+void menu();
+void instructions();
+void pause();
+void game_over();
+void draw_arrow(int x, int y);
+void timer_isr();
+void buttonA_isr();
+void display_lives(int lives);
 
 int main()
 {
-    // initialisation
-    lcd.init();
-    road.init();
-    car.init();
-    joystick.init();
-    lcd.setContrast(0.5);
-   
-    float i = 0;
-    float offset = 0;
-    float start_pos = 38.0;
-    float y = 0.0;
-    float it = 0.0;
-    float perspective = 0.0;
+    button_A.rise(&buttonA_isr);
+    init_buttons();
+    int state = 0;  // set inital state
+    init();
+    // welcome();
+    int up_down = 17;
+    float number_1 = 0;
+    float number_2 = 0;
+    int lives = 4;
+    int old_collision = 0;
+    display_lives(0);
+    
+    // set-up the ticker so that the ISR it is called every 5 seconds
+    ticker.attach(&timer_isr,5);
     
     while(1) {
         lcd.clear();
-        road.draw(lcd, utils, i, offset);
-        
-        if (button_A.read() == 1) {
+        Vector2D coord = joystick.get_mapped_coord(); 
+        switch(state) {
             
-            road.draw_warn(lcd, 1);  
-            i += speed;  
-        }
-        
-        if (button_C.read() == 1) {
-            road.draw_warn(lcd, 2);
-            i -= speed;   
-        }
-        offset += road_speed;
-        
-        Vector2D coord = joystick.get_mapped_coord();
-        int direction = car.get_direction(coord.x);
-        int magnitude = car.get_magnitude(coord.x);
-        
-        if(direction == STRAIGHT) {
-            y = 0;
+            case 0: // Main Menu
+            {
+                menu();
+                lives = 4;
+                display_lives(0);
+                if(coord.y >= 0.5) {
+                    up_down = 17;
+                }
+                
+                if(coord.y <= -0.5) {
+                    up_down = 25;
+                }
+                
+                draw_arrow(2,up_down);
+                
+                if(g_buttonA_flag) {
+                    if(up_down == 17){
+                        state = 1;
+                        g_buttonA_flag = 0;
+                    }
+                    if(up_down == 25) {
+                        state = 2;
+                        g_buttonA_flag = 0;
+                    }
+                }  
+                break;
             }
-        
-        if(direction == LEFT) {
-            if(magnitude == 1) {
-                y = 4.0;
-                }
-            if(magnitude == 2) {
-                y = 4.0;
+            
+            
+            case 1: // Actual Game
+            {
+                game.update(coord.x);
+                
+                // check if flag is set i.e. interrupt has occured
+                if (g_timer_flag) {
+                    g_timer_flag = 0;  // if it has, clear the flag
+                    
+                    // generate random road or ball
+                    // initialize random seed: 
+                    srand (time(NULL));
+                    
+                    number_1 = (rand() % 20 + 1) - 10;
+                    number_2 = (static_cast <float> (rand()) / (static_cast <float> (RAND_MAX/10.0))) - 5.0;
+                    game.reset_ball();
                 }
-            start_pos -= car_speed;
-        } 
-              
-        if(direction ==  RIGHT) {
-            if(magnitude == 1) {
-                y = 4.0;
+                
+                game.generate_ball(number_1);
+                game.generate_road(number_2);
+         
+                // put the MCU to sleep until an interrupt wakes it up
+                sleep();
+                render();
+                display_lives(lives);
+                
+                int collision = game.check_collision();
+                if(collision == 1 && old_collision == 0) {
+                    lives--;
+                    if (lives == 0) {
+                        state = 4;
+                    }
+                }
+                
+                old_collision = collision;
+                
+                if(g_buttonA_flag) {
+                    state = 3;
+                    g_buttonA_flag = 0;   
+                }
+                break;
+            }
+            
+           
+            case 2:  // Instructions 
+            {
+                instructions();
+                if(g_buttonA_flag) {
+                    state = 0;
+                    g_buttonA_flag = 0;
                 }
-            if(magnitude == 2) {
-                y = 8.0;
+                break;   
+            }
+                 
+                
+            case 3: // Pause Menu
+            {
+                pause();
+                if(coord.y >= 0.5) {
+                    up_down = 17;    
                 }
-            start_pos += car_speed;
-        }
-        
-        // define boundaries for car movement
-         if(start_pos < 20.0) {
-            start_pos = 20.0;
+                if(coord.y <= -0.5) {
+                    up_down = 25;
+                    }    
+                
+                draw_arrow(2,up_down);
+                if(g_buttonA_flag) {
+                    if(up_down == 17){
+                        state = 1;
+                        g_buttonA_flag = 0;
+                    }
+                    if(up_down == 25) {
+                        state = 0;
+                        g_buttonA_flag = 0;
+                    } 
+                }
+                break;
+            }
+            
+            case 4: // Game over
+            {
+                game_over();
+                if(g_buttonA_flag) {
+                    state = 0;
+                    g_buttonA_flag = 0;
+                }
+                break;  
+            } 
         }
-        
-        if(start_pos > 55.0) {
-            start_pos = 55.0;
-        }
-        
-        car.draw(lcd,start_pos,y);
-     
-        std::vector<Vector2Df> path_points = utils.getCurve(12,60,25,30,28,12);
-        float iterator = path_points.size() - it;
-        
-        // take the current coordinates and draw circle on the line
-        ball.draw(lcd, path_points, 4, iterator);
-        it += 0.03;
-        perspective += road_speed;  
-              
-        printf("%f\n", it);
-        
-        if (it > 10) {
-            it = 0;
-        }
-        
-        lcd.refresh(); 
+    sleep();
+    lcd.refresh();
     }
 }
 
@@ -132,5 +212,101 @@
     // PCB has external pull-down resistors so turn the internal ones off
     // (default for DigitalIn)
     button_A.mode(PullNone);
+    button_B.mode(PullNone);
     button_C.mode(PullNone);
+    button_D.mode(PullNone);
+}
+
+
+void init() {
+    seven_seg.write(0x00); 
+    lcd.init();
+    joystick.init();
+    lcd.setContrast(0.55);
+    
+    std::vector<Vector2Df> curve_point;
+    Vector2Df point = {-20.0,-50.0};
+    curve_point.push_back(point);
+    
+    game.init(0.0,          // road direction
+              0.0,          // road inclination
+              4,            // ball radius
+              curve_point,  // points from ball path
+              0,            // car direction
+              0,            // car magnitude 
+              38.0,         // car start position
+              0.0,          // offset
+              0.0,          // car_turn
+              0.0);         // ball_it
+}
+
+void menu() { 
+    lcd.printString("MAIN MENU",15,0);
+    lcd.printString("Start Game",10,2);
+    lcd.printString("Instructions",10,3); 
+    lcd.printString("Press A",20,5); 
+}
+
+void instructions() {  
+    lcd.printString("INSTRUCTIONS",6,0);
+    lcd.printString("Use joystick",6,1);
+    lcd.printString("to move",21,2);
+    lcd.printString("Press A to",13,4);
+    lcd.printString("pause or exit",4,5);
+    display_lives(0);
+}
+
+void pause() { 
+    lcd.printString("PAUSE",27,0);
+    lcd.printString("Back to game",10,2);
+    lcd.printString("Back to menu",10,3); 
+}
+
+void game_over() {
+    lcd.printString("GAME OVER",16,1); 
+    lcd.printString("Don't collide!",1,2); 
+    lcd.printString("Press A",20,5); 
+}
+
+void render() { 
+    game.draw(lcd, utils);
+}
+
+void draw_arrow(int x, int y) {
+    const int arrow[5][7] = {
+        { 0, 0, 0, 0, 1, 0, 0 },
+        { 1, 1, 1, 1, 1, 1, 0 },
+        { 1, 1, 1, 1, 1, 1, 1 },
+        { 1, 1, 1, 1, 1, 1, 0 },
+        { 0, 0, 0, 0, 1, 0, 0 },
+    };  
+    lcd.drawSprite(x,y,5,7,(int *)arrow);
+}
+
+// time-triggered interrupt
+void timer_isr() {
+    g_timer_flag = 1;   // set flag in ISR
+}
+
+// Button A event-triggered interrupt
+void buttonA_isr() {
+    g_buttonA_flag = 1;   // set flag in ISR     
+}
+
+void display_lives(int lives) {
+    if (lives == 4) {
+        lcd.printString("Lives: 4",0,0);
+        seven_seg.write(0x66);
+    } else if (lives == 3) {
+        lcd.printString("Lives: 3",0,0);
+        seven_seg.write(0x4F);
+    } else if (lives == 2) {
+        lcd.printString("Lives: 2",0,0);
+        seven_seg.write(0x5B);
+    } else if (lives == 1) {
+        lcd.printString("Lives: 1",0,0);
+        seven_seg.write(0x06);
+    } else {
+        seven_seg.write(0x3F);
+    }
 }
\ No newline at end of file