This is a ball on the Mbed.

Dependencies:   4DGL-uLCD-SE IMUfilter ITG3200 Music mbed-rtos mbed

main.cpp

Committer:
Strikewolf
Date:
2014-03-14
Revision:
0:680348a938f8

File content as of revision 0:680348a938f8:

        #include <math.h>
    #include <time.h>
     
    #include "mbed.h"
    #include "uLCD_4DGL.h"
    #include "rtos.h"
    #include "IMU_RPY.h"
     
    // game configuration macros
    #define NUMBER_OF_ROUNDS 3
    #define CURSOR_SPEED 0.5
    #define CURSOR_COLOR 0xFFFF00
    #define SPACE_COLOR BLACK
    #define WALL_COLOR RED
    #define START_COLOR GREEN
    #define END_COLOR BLUE
     
    // game mechanism macros
    #define MAZE_DIMENSION 25
    #define SCALAR (125 / MAZE_DIMENSION)
    #define START_BLOCK 3
    #define END_BLOCK 2
    #define WALL_BLOCK 1
    #define SPACE_BLOCK 0
     
    // prototypes in alphabetical order
    bool checkVictory();
    void displaySplashScreen();
    void displayMaze();
    void displayVictory();
    void displayEndGame();
    void generateMaze();
    void depthFirstSearch(int r, int c);
    void updateVelocity();
    void updateBall();
    void xthread(void const *args);
    void ythread(void const *args);
     
    // display variables
    uLCD_4DGL uLCD(p9, p10, p11);
     
    // cursor variables
    Mutex x_mutex, y_mutex;
    char cursor_x_pos, cursor_y_pos;
    char old_cursor_x_pos, old_cursor_y_pos;
    float ballxvel, ballyvel;
     
    // start and end variables
    char start_x, start_y;
    char end_x, end_y;
     
    // maze data structure
    char maze[MAZE_DIMENSION][MAZE_DIMENSION];
     
    // level counter
    char level = 0;
     
    int main()
    {
        pc.printf("Super Mbed Ball!!!!!\n\r");
     
        // set up gyroscope/accelerometer (rpy = roll, pitch, yaw)
        rpy_init();
     
        //Overclock uLCD
        uLCD.baudrate(3000000);
     
        //Title Screen
        displaySplashScreen();
     
        //Start physics engine threads
        Thread t1(xthread);
        Thread t2(ythread);
     
        // each iteration runs a complete game until user wins.
        // wait for user to complete max allowed levels before
        // terminating
        while(level < NUMBER_OF_ROUNDS) {
     
            //Initial velocity
            ballxvel = 0;
            ballyvel = 0;
     
            //create Maze
            generateMaze();
            displayMaze();
     
            //Game Execution Loop
            while (checkVictory() == false) {
                updateVelocity();
                updateBall();
            }
     
            // victory splash screen
            displayVictory();
           
            // increment level counter
            level++;
        }
       
        // display last end game screen before exiting program
        displayEndGame();
    }
     
    // checks if cursor has  moved to the end block
    bool checkVictory()
    {
        // check if cursor made it to end row and column
        return(maze[cursor_x_pos][cursor_y_pos] == END_BLOCK);
    }
     
    // depth first search, called by generateMaze()
    void depthFirstSearch(int r, int c)
    {
        // 4 random direction
        int randDirs1[] = {1, 2, 3, 4};
        int randDirs2[] = {4, 2, 3, 1};
        int *randDirs = NULL;
     
        if (rand() % 2)
            randDirs = randDirs1;
        else
            randDirs = randDirs2;
     
        // Examine each direction
        for (int i = 0; i < 4; i++) {
            switch (randDirs[i]) {
                case 1: // Up
                    // Whether 2 cells up is out or not
                    if (r - 2 <= 0)
                        continue;
                    if (maze[r - 2][c] != 0) {
                        maze[r - 2][c] = SPACE_BLOCK;
                        maze[r - 1][c] = SPACE_BLOCK;
                        depthFirstSearch(r - 2, c);
                    }
                    break;
                case 2: // Right
                    // Whether 2 cells to the right is out or not
                    if (c + 2 >= MAZE_DIMENSION - 1)
                        continue;
                    if (maze[r][c + 2] != 0) {
                        maze[r][c + 2] = SPACE_BLOCK;
                        maze[r][c + 1] = SPACE_BLOCK;
                        depthFirstSearch(r, c + 2);
                    }
                    break;
                case 3: // Down
                    // Whether 2 cells down is out or not
                    if (r + 2 >= MAZE_DIMENSION - 1)
                        continue;
                    if (maze[r + 2][c] != 0) {
                        maze[r + 2][c] = SPACE_BLOCK;
                        maze[r + 1][c] = SPACE_BLOCK;
                        depthFirstSearch(r + 2, c);
                    }
                    break;
                case 4: // Left
                    // Whether 2 cells to the left is out or not
                    if (c - 2 <= 0)
                        continue;
                    if (maze[r][c - 2] != 0) {
                        maze[r][c - 2] = SPACE_BLOCK;
                        maze[r][c - 1] = SPACE_BLOCK;
                        depthFirstSearch(r, c - 2);
                    }
                    break;
            }
        }
    }
     
    //Take whats in the mazearr and write it
    void displayMaze()
    {
     
        // Clear previous maze
        uLCD.filled_rectangle(0, 0, 127, 127, SPACE_COLOR);
     
        // display start location
        uLCD.filled_rectangle(start_x * SCALAR, start_y * SCALAR,
                              (start_x + 1) * SCALAR - 1, (start_y + 1) * SCALAR - 1, START_COLOR);
     
        // display end location
        uLCD.filled_rectangle(end_x * SCALAR, end_y * SCALAR,
                              (end_x + 1) * SCALAR - 1, (end_y + 1) * SCALAR - 1, END_COLOR);
     
        // display walls of maze
        for (int i = 0; i < MAZE_DIMENSION; i++) {
            for (int j = 0; j < MAZE_DIMENSION; j++) {
                if (maze[i][j] == WALL_BLOCK)
                    uLCD.filled_rectangle(i * SCALAR, j * SCALAR, (i + 1) * SCALAR - 1, (j + 1) * SCALAR - 1, WALL_COLOR);
            }
        }
    }
     
    //Game title screen
    void displaySplashScreen()
    {
        uLCD.text_width(2);
        uLCD.text_height(2);
        uLCD.locate(0, 0);
        uLCD.printf("SuperMbed");
        uLCD.text_width(2);
        uLCD.text_height(3);
        uLCD.locate(2, 1);
        uLCD.printf("Ball!");
        wait(3);
    }
     
    //Victory screen - 5 sec delay and then next level
    void displayVictory()
    {
        uLCD.text_width(2);
        uLCD.text_height(2);
        uLCD.locate(1, 3);
        uLCD.printf("VICTORY!");
        wait(5);
    }
     
    // End game screen
    void displayEndGame() {
        // wipe screen
        uLCD.filled_rectangle(0, 0, 127, 127, BLACK);
       
        // write game over
        uLCD.text_width(2);
        uLCD.text_height(2);
        uLCD.locate(1, 3);
        uLCD.printf("GAME OVER");
    }
     
    // randomely generates a maze using depth first search
    void generateMaze()
    {
        // Initialize all spaces to walls
        for (int i = 0; i < MAZE_DIMENSION; i++)
            for (int j = 0; j < MAZE_DIMENSION; j++)
                maze[i][j] = WALL_BLOCK;
     
        // unused z-value of gyroscope will be random seed
        srand(imuFilter.getYaw());
     
        // calculate starting row and column of maze DFS
        // starting row and column must be an odd number
        int seed = 0;
        while(seed % 2 == 0)
            seed = rand() % (MAZE_DIMENSION -1) + 1;
     
        pc.printf("seed: %d\r\n", seed);
     
        // Starting cell
        maze[seed][seed] = SPACE_BLOCK;
     
        // Allocate the maze with recursive method
        depthFirstSearch(seed, seed);
     
        // find start and end positions
        start_x = start_y = 0xF;
        end_x = end_y = 0;
        for (int i = 0; i < MAZE_DIMENSION; i++) {
            for (int j = 0; j < MAZE_DIMENSION; j++) {
     
                if (maze[i][j] == SPACE_BLOCK) {
                    // start space
                    if((i*MAZE_DIMENSION + j) < (start_x*MAZE_DIMENSION + start_y)) {
                        start_x = i;
                        start_y = j;
                    }
     
                    // end space
                    if((i*MAZE_DIMENSION + j) > (end_x*MAZE_DIMENSION + end_y)) {
                        end_x = i;
                        end_y = j;
                    }
                }
            }
        }
     
        // reset cursor starting position
        cursor_x_pos = old_cursor_x_pos = start_x;
        cursor_y_pos = old_cursor_y_pos = start_y;
     
        // mark spots in maze data structure
        maze[start_x][start_y] = START_BLOCK;
        maze[end_x][end_y] = END_BLOCK;
     
    }
     
    //Move the ball around and draw to the screen
    void updateBall()
    {
        x_mutex.lock();
        y_mutex.lock();
     
        // redraw ball only if the position has changed
        if (cursor_x_pos != old_cursor_x_pos || cursor_y_pos != old_cursor_y_pos) {
     
            //Wipe the old ball
     
            uLCD.filled_rectangle(old_cursor_x_pos * SCALAR, old_cursor_y_pos * SCALAR,
                                  (old_cursor_x_pos + 1) * SCALAR - 1, (old_cursor_y_pos + 1) * SCALAR - 1, SPACE_COLOR);
     
            //Out with the old in with the new!
            uLCD.filled_rectangle(cursor_x_pos * SCALAR, cursor_y_pos * SCALAR,
                                  (cursor_x_pos + 1) * SCALAR - 1, (cursor_y_pos + 1) * SCALAR - 1, CURSOR_COLOR);
     
            // store new position
            old_cursor_x_pos = cursor_x_pos;
            old_cursor_y_pos = cursor_y_pos;
        }
     
        x_mutex.unlock();
        y_mutex.unlock();
    }
     
     
    //This will be where the gyro values are used to accelerate/decelerate the ball
    void updateVelocity()
    {
        x_mutex.lock();
        y_mutex.lock();
     
        // sample gyroscope/accelerometer through filter
        ballxvel = toDegrees(imuFilter.getPitch()) / -10;
        ballyvel = toDegrees(imuFilter.getRoll()) / 10;
     
        // bound velocities to max speed for x
        if (ballxvel > 1)
            ballxvel = CURSOR_SPEED;
        else if (ballxvel < -1)
            ballxvel = -CURSOR_SPEED;
     
        // bound velocities to max speed for y
        if (ballyvel > 1)
            ballyvel = CURSOR_SPEED;
        else if (ballyvel < -1)
            ballyvel = -CURSOR_SPEED;
     
        // round to 2 decimal places
        ballxvel = floorf(ballxvel * 100.0) / 100.0;
        ballyvel = floorf(ballyvel * 100.0) / 100.0;
     
        x_mutex.unlock();
        y_mutex.unlock();
    }
     
     
    //xthread and ythread act as the physics engine, simulating velocity and wall detection
    void xthread(const void* args)
    {
        while (1) {
            x_mutex.lock();
            y_mutex.lock();
            if (ballxvel > 0) {
                if (maze[cursor_x_pos + 1][cursor_y_pos] != WALL_BLOCK)
                    cursor_x_pos++;
            } else if (ballxvel < 0) {
                if (maze[cursor_x_pos - 1][cursor_y_pos] != WALL_BLOCK)
                    cursor_x_pos--;
            }
            x_mutex.unlock();
            y_mutex.unlock();
            Thread::wait(100 - 98 * abs(ballxvel));
        }
    }
     
    void ythread(const void* args)
    {
        while (1) {
            x_mutex.lock();
            y_mutex.lock();
            if (ballyvel > 0) {
                if (maze[cursor_x_pos][cursor_y_pos + 1] != WALL_BLOCK)
                    cursor_y_pos++;
            } else if (ballyvel < 0) {
                if (maze[cursor_x_pos][cursor_y_pos - 1] != WALL_BLOCK)
                    cursor_y_pos--;
            }
            x_mutex.unlock();
            y_mutex.unlock();
            Thread::wait(100 - 98 * abs(ballyvel));
        }
    }