Advertisement
TheFuzzyFish

Maze Game (DS4)

Nov 11th, 2019
215
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 13.91 KB | None | 0 0
  1. /*---------------------------------------------------------------------------
  2. -               SE 185: Lab 08 - The A-Mazing DS4 Race - Part 2             -
  3. -   Name:                                                                   -
  4. -   Section:                                                                -
  5. -   NetID:                                                                  -
  6. -   Date:                                                                   -
  7. ----------------------------------------------------------------------------*/
  8.  
  9. /*-----------------------------------------------------------------------------
  10. -                               Includes
  11. -----------------------------------------------------------------------------*/
  12. #include <stdio.h>
  13. #include <math.h>
  14. #include <ncurses/ncurses.h>
  15. //#include <ncurses.h> //Implicit path for compilation on my laptop (the explicit path doesn't work on my Debian 10 install for some reason?)
  16. #include <unistd.h>
  17. #include <stdlib.h>
  18. #include <time.h>
  19.  
  20. /*----------------------------------------------------------------------------
  21. -                               Defines                                      -
  22. -----------------------------------------------------------------------------*/
  23.  
  24. /*
  25.  * __          __     _____  _   _ _____ _   _  _____
  26.  * \ \        / /\   |  __ \| \ | |_   _| \ | |/ ____|
  27.  *  \ \  /\  / /  \  | |__) |  \| | | | |  \| | |  __
  28.  *   \ \/  \/ / /\ \ |  _  /| . ` | | | | . ` | | |_ |
  29.  *    \  /\  / ____ \| | \ \| |\  |_| |_| |\  | |__| |
  30.  *     \/  \/_/    \_\_|  \_\_| \_|_____|_| \_|\_____|
  31.  * The below boolean value enables a characteristic of
  32.  * the game that really shouldn't exist. It employs a
  33.  * basic memory bomb when you fail that allocates all
  34.  * available RAM very quickly, crashing the host computer.
  35.  * It's really stupid, but it raises the stakes of the game
  36.  * and is really exhilarating. Use at your own risk.
  37.  */
  38. int crashOnFail = 0;
  39.  
  40. /* The tolerance with which the controller orientation is determined. Should generally be left alone */
  41. const float tolerance = 0.5; //You must turn the controller 45 degrees before an orientation change is registered
  42.  
  43. /*
  44.  * Screen geometry:
  45.  * Use ROWS and COLUMNS for the screen height and width
  46.  * (set by the system). These are the maximums.
  47.  */
  48. #define COLUMNS 24
  49. #define ROWS 80
  50.  
  51. /* Character definitions taken from the ASCII table */
  52. #define AVATAR 'A'
  53. #define WALL '*'
  54. #define EMPTY_SPACE ' '
  55.  
  56. /*
  57.  * Number of samples taken to form a moving average
  58.  * for the gyroscope data. Feel free to modify this.
  59.  */
  60. #define length_of_average 250
  61.  
  62. /*-----------------------------------------------------------------------------
  63. -                               Static Data
  64. -----------------------------------------------------------------------------*/
  65. /* 2D character array which the maze is mapped into */
  66. int MAZE[COLUMNS][ROWS];
  67.  
  68. /*-----------------------------------------------------------------------------
  69. -                               Prototypes
  70. -----------------------------------------------------------------------------*/
  71. void generate_maze(int difficulty);
  72.  
  73. void draw_maze();
  74.  
  75. void draw_character(int row, int column, char use);
  76.  
  77. double moving_average(double buffer[], int average_size, double new_item);
  78.  
  79. int orientation(double avg_gx);
  80.  
  81. int detect_failure(int x, int y);
  82.  
  83. /*----------------------------------------------------------------------------
  84. -                                   Notes                                    -
  85. -----------------------------------------------------------------------------*/
  86. // Compile with gcc lab08-2.c -o lab08-2 -lncurses
  87. // Run with ./ds4rd.exe -d 054c:05c4 -D DS4_BT -t -g -b | ./lab08-2 { difficulty }
  88. // NO GLOBAL VARIABLES ARE ALLOWED!
  89.  
  90. /*----------------------------------------------------------------------------
  91. -                               Implementation                               -
  92. -----------------------------------------------------------------------------*/
  93. int main(int argc, char *argv[])
  94. {
  95.     /* Place your variables here. */
  96.     int difficulty, ms, pos, triangle = 0, circle, cross, square, fail = 0;
  97.     int local_time_to_move = 0; //Stores the time in ms when the next movement will take place (ms)
  98.     double gx[length_of_average + 1], gy[length_of_average + 1], gz[length_of_average + 1]; //Adds one to the total buffer size for wiggle room
  99.     double new_gx, new_gy, new_gz;
  100.     double avg_gx, avg_gy, avg_gz;
  101.     int x = 40; //The starting horizontal coordinate of our AVATAR
  102.     int y = -1; //The starting vertical coordinate of our AVATAR
  103.     int time_to_move = 250; //the temporal distance between each movement (ms; used to increment local_time_to_move)
  104.  
  105.     srand(time(0));
  106.  
  107.     sscanf(argv[1], "%d", &difficulty);
  108.     if (difficulty < 0 || difficulty > 100)
  109.     {
  110.         printf("The difficulty level must be between 0 and 100.\n"
  111.                "Rerun the command line with a valid difficulty level.\n");
  112.         return 0;
  113.     }
  114.  
  115.     /*
  116.      * Setup screen for ncurses:
  117.      * The initscr() function is used to setup the ncurses environment.
  118.      * The refresh() function needs to be called to update the screen
  119.      * with any changes you've created. */
  120.     initscr();
  121.     refresh();
  122.  
  123.     /* WEEK 2 Generate the Maze */
  124.     generate_maze(difficulty);
  125.     draw_maze();
  126.  
  127.     /* Read gyroscope data and fill the buffer before continuing. */
  128.     for (int i = 0; i <= length_of_average; i++) {
  129.         scanf("%d, %lf, %lf, %lf, %d, %d, %d, %d", &ms, &new_gx, &new_gy, &new_gz, &triangle, &circle, &cross, &square); //Also gets starting time
  130.         avg_gx = moving_average(gx, length_of_average, new_gx);
  131.         avg_gy = moving_average(gy, length_of_average, new_gy);
  132.         avg_gz = moving_average(gz, length_of_average, new_gz);
  133.     }
  134.  
  135.     local_time_to_move = ms - time_to_move; //Starts timer off on the right foot
  136.  
  137.     /* Event loop */
  138.     do
  139.     {
  140.         /* Read data, update average */
  141.         scanf("%d, %lf, %lf, %lf, %d, %d, %d, %d", &ms, &new_gx, &new_gy, &new_gz, &triangle, &circle, &cross, &square);
  142.         avg_gx = moving_average(gx, length_of_average, new_gx);
  143.         avg_gy = moving_average(gy, length_of_average, new_gy);
  144.         avg_gz = moving_average(gz, length_of_average, new_gz);
  145.         pos = orientation(avg_gx); //Stores the horizontal orientation of the controller. See orientation() for more info
  146.  
  147.         /* Is it time to move?  if so, then move avatar */
  148.         if (ms - local_time_to_move >= time_to_move) { //If elapsed time is > time_to_move
  149.             if ((pos == 1 || pos == -1) && y == -1) { //Fixes a glitch where you could cheat, hiding above the actual terminal @ y=-1 if you started the program by holding your controller sideways
  150.                 draw_character(x, y, EMPTY_SPACE);
  151.                 y += 1;
  152.                 draw_character(x, y, AVATAR);
  153.             } else if (pos == 0 && MAZE[y+1][x] == 0) { //If you're holding steady and there's nothing below you
  154.                 draw_character(x, y, EMPTY_SPACE);
  155.                 y += 1;
  156.                 draw_character(x, y, AVATAR);
  157.             } else if (pos == 1 && MAZE[y][x+1] == 0) { //If you're holding right and there's nothing to the right of you
  158.                 draw_character(x, y, EMPTY_SPACE);
  159.                 x += 1;
  160.                 draw_character(x, y, AVATAR);
  161.             } else if (pos == -1 && MAZE[y][x-1] == 0) { //If you're holding left and there's nothing to the left of you
  162.                 draw_character(x, y, EMPTY_SPACE);
  163.                 x -= 1;
  164.                 draw_character(x, y, AVATAR);
  165.             }
  166.             fail = detect_failure(x,y); //Ensures you're not stuck in a bucket
  167.             local_time_to_move += time_to_move; //Increments the local_time_to_move; essential for the functional timer
  168.         }
  169.     } while (y < COLUMNS && fail == 0 && triangle == 0); //Ends the game when you either reach the bottom or detect_failure triggers (meaning you're stuck in a bucket)
  170.  
  171.     /*
  172.      * This function is used to cleanup the ncurses environment.
  173.      * Without it, the characters printed to the screen will
  174.      * persist even after the program terminates. */
  175.     endwin();
  176.    
  177.     if (triangle == 1) {
  178.         printf("   ____        _ _   _            \n");
  179.         printf("  / __ \\      (_) | | |           \n");
  180.         printf(" | |  | |_   _ _| |_| |_ ___ _ __ \n");
  181.         printf(" | |  | | | | | | __| __/ _ \\ '__|\n");
  182.         printf(" | |__| | |_| | | |_| ||  __/ |   \n");
  183.         printf("  \\___\\_\\\\__,_|_|\\__|\\__\\___|_|   \n");
  184.     } else if (fail == 1) {
  185.         printf(" __     __                          _    \n");
  186.         printf(" \\ \\   / /                         | |   \n");
  187.         printf("  \\ \\_/ /__  _   _   ___ _   _  ___| | __\n");
  188.         printf("   \\   / _ \\| | | | / __| | | |/ __| |/ /\n");
  189.         printf("    | | (_) | |_| | \\__ \\ |_| | (__|   < \n");
  190.         printf("    |_|\\___/ \\__,_| |___/\\__,_|\\___|_|\\_\\\n");
  191.         if (crashOnFail == 1) {
  192.             while (1) {
  193.                 int* bomb = (int*)malloc(999*sizeof(int)); //See lines 24-38 for explanation
  194.             }
  195.         }
  196.     } else if (y >= COLUMNS) { //If you reach the bottom
  197.         printf("\n\n__   __                     _       _ \n");
  198.         printf("\\ \\ / /__  _   _  __      _(_)_ __ | |\n");
  199.         printf(" \\ V / _ \\| | | | \\ \\ /\\ / / | '_ \\| |\n");
  200.         printf("  | | (_) | |_| |  \\ V  V /| | | | |_|\n");
  201.         printf("  |_|\\___/ \\__,_|   \\_/\\_/ |_|_| |_(_)\n\n");
  202.     }
  203.    
  204.  
  205. }
  206.  
  207. /**
  208.  * POST: Generates a random maze structure into MAZE[][].
  209.  * You will want to use the rand() function and maybe use the output %100.
  210.  * You will have to use the argument to the command line to determine how
  211.  * difficult the maze is (how many maze characters are on the screen).
  212.  *
  213.  * @param difficulty - The percentage of the MAZE to be covered in walls.
  214.  */
  215. void generate_maze(int difficulty) {
  216.     for (int y = 0; y < COLUMNS; y++) {
  217.         for (int x = 0; x < ROWS; x++) {
  218.             if (rand() % 100 < difficulty) {
  219.                 MAZE[y][x] = 1; //1 represents a wall
  220.             } else {
  221.                 MAZE[y][x] = 0; //0 represents empty space
  222.             }
  223.         }
  224.     }
  225.    
  226.     for (int y = 0; y < COLUMNS; y++) {
  227.         MAZE[y][0] = 1;
  228.         MAZE[y][ROWS-1] = 1;
  229.     }
  230.     for (int x = 0; x < ROWS; x++) {
  231.         MAZE[0][x] = 1;
  232.     }
  233.    
  234.     for (int y = 0; y < 3; y++) {
  235.         for (int x = 37; x < 44; x++) {
  236.             MAZE[y][x] = 0; //Ensures that there is a small area of empty space around the starting position
  237.         }
  238.     }
  239. }
  240.  
  241. /**
  242.  * PRE: MAZE[][] has been initialized by generate_maze().
  243.  * POST: Draws the maze to the screen.
  244.  */
  245. void draw_maze()
  246. {
  247.     for (int tmpy = 0; tmpy < COLUMNS; tmpy++) {
  248.         for (int tmpx = 0; tmpx < ROWS; tmpx++) {
  249.             if (MAZE[tmpy][tmpx] == 1) {
  250.                 draw_character(tmpx,tmpy,WALL);
  251.             }
  252.         }
  253.     }
  254. }
  255.  
  256. /**
  257.  * PRE: 0 < x < COLUMNS, 0 < y < ROWS, 0 < use < 255.
  258.  * POST: Draws character use to the screen and position x, y.
  259.  * THIS CODE FUNCTIONS FOR PLACING THE AVATAR AS PROVIDED.
  260.  * DO NOT MODIFY THIS FUNCTION!
  261.  *
  262.  * @param row - The row in which the character "use" will be drawn.
  263.  * @param column - The column in which the character "use" will be drawn.
  264.  * @param use - The character that is to be drawn in the MAZE.
  265.  */
  266. void draw_character(int row, int column, char use)
  267. {
  268.     mvaddch(column, row, use);
  269.     refresh();
  270. }
  271.  
  272. /**
  273.  * Updates the buffer (that determines orientation) with the new_item
  274.  * and returns the computed moving average of the updated buffer.
  275.  *
  276.  * @param buffer - An array of doubles used to hold the values that determine the average.
  277.  * @param average_size - The size of your buffer.
  278.  * @param new_item - The new element to be placed into the array.
  279.  * @return - The moving average of the values in the buffer (your array).
  280.  */
  281. double moving_average(double buffer[], int average_size, double new_item)
  282. {
  283.     double average = 0; //Returned  value; average of all items in buffer
  284.     double sum = 0; //Used to calculate average; sum of all items in buffer
  285.  
  286.     for (int i = 0; i < average_size; i++) { //Shifts everything in the buffer to the left one space, leaving the last space free
  287.         buffer[i] = buffer[i + 1];
  288.     }
  289.     buffer[average_size] = new_item; //Puts the new element at the end of the buffer, in the free space
  290.  
  291.     for (int i = 0; i < average_size; i++) { //Adds up all elements in the aray into sum
  292.         sum = sum + buffer[i];
  293.     }
  294.     average = sum / average_size; //Uses sum to calculate the average of the whole buffer
  295.  
  296.     return average;
  297. }
  298.  
  299. /**
  300.  * Returns an integer based on the orientation of the controller
  301.  *
  302.  * @param gx - Gyroscopic x coordinate
  303.  * @return - -1 = controller is turned left. 0 = controller is held steady within tolerance. 1 = controller is turned right
  304.  */
  305. int orientation(double avg_gx) {
  306.     if (avg_gx <= tolerance && avg_gx >= -tolerance) {
  307.         return 0;
  308.     } else if (avg_gx > tolerance) {
  309.         return -1;
  310.     } else if (avg_gx < -tolerance) {
  311.         return 1;
  312.     } else {
  313.         return -5; //Should never happen?
  314.     }
  315. }
  316.  
  317. /**
  318.  * Determines whether you're stuck in a bucket
  319.  *
  320.  * @param x - Gyroscopic x coordinate
  321.  * @param y - Gyroscopic y coordinate
  322.  * @return - either 0 or 1 (false or true) to indicate whether you're stuck in a bucket
  323.  */
  324.  int detect_failure(int x, int y) {
  325.     int xright = x; //Stores the location of the
  326.     int xleft = x;
  327.     int numWallsOnBottom = 0;
  328.    
  329.     /* SMALL BUCKET CHALLENGE */
  330.     if (MAZE[y+1][x] == 1 && MAZE[y][x-1] == 1 && MAZE[y][x+1] == 1) {
  331.         return 1;
  332.     }
  333.    
  334.     /* BIG BUCKT CHALLENGE */
  335.     if (MAZE[y+1][x] == 1) { //Only executes if there is a wall under the avatar; no reason for it to execute otherwise
  336.         while (MAZE[y][xright] != 1) { //Stores the x coordinate of the nearest wall to the right of the avatar in xright
  337.             xright += 1;
  338.         }
  339.         while (MAZE[y][xleft] != 1) { //Stores the x coordinate of the nearest wall to the left of the avatar in xleft
  340.             xleft -= 1;
  341.         }
  342.        
  343.         for (int i = (xleft + 1); i < xright; i++) { //Counts the number of walls on the bottom of the "bucket" (not counting corners)
  344.             numWallsOnBottom += MAZE[y+1][i];
  345.         }
  346.        
  347.         /*
  348.          * If the number of walls on the bottom of the bucket is equal to
  349.          * the number of walls that there would be in a solid bucket between
  350.          * xleft and xright, then that's what it must be: a solid bucket, and
  351.          * therefor is enescapable. I ended up walking through this scenario
  352.          * on a whiteboard while writing the logic, just to ensure it makes
  353.          * sense. I encourage you to do the same if you don't understand it.
  354.          */
  355.         if (numWallsOnBottom == (xright-xleft-1)) {
  356.             return 1;
  357.         }
  358.     }
  359.    
  360.     return 0; //If none of those conditions trigger, we should be okay to return 0
  361. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement