This is a ball on the Mbed.
Dependencies: 4DGL-uLCD-SE IMUfilter ITG3200 Music mbed-rtos mbed
Diff: main.cpp
- Revision:
- 0:680348a938f8
diff -r 000000000000 -r 680348a938f8 main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Fri Mar 14 17:03:16 2014 +0000 @@ -0,0 +1,384 @@ + #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)); + } + } +