From 133718a7a2a05315cbaefe7c0be609a7b3e29348 Mon Sep 17 00:00:00 2001 From: Jidong Xiao Date: Mon, 16 Oct 2023 16:02:42 -0400 Subject: [PATCH] adding lab 8 --- labs/08_recursion/README.md | 158 ++++++++++++++++++++++++++++++++++++ labs/08_recursion/grid1.txt | 7 ++ labs/08_recursion/grid2.txt | 11 +++ labs/08_recursion/grid3.txt | 28 +++++++ labs/08_recursion/grid4.txt | 41 ++++++++++ labs/08_recursion/start.cpp | 130 +++++++++++++++++++++++++++++ 6 files changed, 375 insertions(+) create mode 100644 labs/08_recursion/README.md create mode 100644 labs/08_recursion/grid1.txt create mode 100644 labs/08_recursion/grid2.txt create mode 100644 labs/08_recursion/grid3.txt create mode 100644 labs/08_recursion/grid4.txt create mode 100644 labs/08_recursion/start.cpp diff --git a/labs/08_recursion/README.md b/labs/08_recursion/README.md new file mode 100644 index 0000000..3d13873 --- /dev/null +++ b/labs/08_recursion/README.md @@ -0,0 +1,158 @@ +# Lab 8 — Recursion + +This lab gives you practice in the use of recursion to solve problems. All three checkpoints addressed in +this lab deal with finding and counting the number of paths between points on a rectilinear grid. A starting +point (x, y) with non-negative integer coordinates is provided. You are only allowed to move horizontally +and vertically along the grid. Hence, from (x, y) you may move to (x + 1, y), (x − 1, y), (x, y − 1), (x, y + 1). +Your goal is to return to the origin (0, 0) in such a way that you never increase the distance to the origin. +The distance is counted as the minimum number of total vertical and horizontal steps to reach the origin. In +the first checkpoint the grid will be “free”. In the second and third checkpoints, some of the grid locations +will be “blocked” in the sense that you can not move to that point. + +Stop now and play with small examples. Draw pictures of a grid. Think about the implications of the rules +before you proceed with the checkpoints. + +## Checkpoint 1 +*estimate: 10-30 minutes* +Did you notice that the rules prevent certain moves from occurring? What are they? If you don’t get them +right you will not be able to do the lab correctly. Confirm your understanding with one of the lab TAs. + +Now, write a simple recursive program (from scratch) that reads a starting location, (x, y) and returns the +total number of different paths back to the origin when the entire grid is “free”. Two paths are different if +there is at least one step on the path that is different even if most of the steps are the same. As a hint, when +x == 0 and y == 0 there is 1 path, when x == 2 and y == 1 there are 3 paths, and when x == 2 and +y == 2 there are 6 paths. When you’re confident your program is debugged try even bigger values. For what +values does the program take a few seconds to complete on your computer? If you increase these values by +1, how does it impact the running time? Is this what you expected from Big O Notation? + +**To complete this checkpoint** show a TA your program to count all paths back to the origin. Be prepared +to discuss the running time of your program for different input values. + +## Checkpoint 2 +*estimate: 20-40 minutes* + +Please download the files: +[start.cpp](start.cpp) +[grid1.txt](grid1.txt) +[grid2.txt](grid2.txt) +[grid3.txt](grid3.txt) +[grid4.txt](grid4.txt) + +Starting from the supplied program, start.cpp, write a program to count the number of paths when some +of the grid locations are blocked. When a location is blocked, no path can go through it. Before writing +your own code, compile and run start.cpp. Use the files grid1.txt, etc. as input. These have a sequence +of blocked locations, ending with the point location (0, 0) (which isn’t blocked, but signals the end of the +input). The next location is the location for the initial (x, y). By changing this location you will be able to +test your program in different ways. + +**To complete this checkpoint** show a TA your code to find and count legal (unblocked) paths. + +## Checkpoint 3 +*estimate: 30-40 minutes* + +Modify your program from Checkpoint 2 so that it finds and outputs a legal path from the given (x, y) +location to the origin. You should both print the sequence of coordinates of a solution path AND modify the +print_grid function to draw the grid a second time with the path marked on the grid with ’$’ symbols. If +there is more than one path, it does not matter which it outputs. Did you notice that all legal paths to the +origin are the same length under the rules provided? +Now let’s explore recursion with gdb/lldb. +Here’s a table of equivalent commands in gdb (Linux, WSL) vs. lldb (Mac, Linux, WSL): +https://lldb.llvm.org/lldb-gdb.html +1. Compile your program with the -g flag, so it includes debug info, including line numbers: + +```console +clang++ -Wall -g start.cpp -o start.out +``` + +2. Start gdb (or lldb): + +```console +gdb ./start.out +``` + +3. Set a breakpoint at the first line of the main function: + +```console +b main +``` + +4. Now run/launch the program. Note: Here’s where we specify the necessary command line arguments. + +```console +r grid2.txt +``` + +5. Place a breakpoint on the line in main where you first call your recursive function. Replace with +the line number in your source code. + +```console +b +``` + +gdb will confirm with a message like this: +Breakpoint 2 at 0xwhatever: file start.cpp, line +You can review your currently set breakpoints: + +```console +info b +``` + +If you happen to mistype the line number, you can delete the breakpoint and try again. is the +breakpoint number with the incorrect line number. + +```console +delete +``` + +6. Place another breakpoint at the first line of your recursive function. And then let the program run +until it reaches the next breakpoint by typing: +continue +When gdb pauses, it should inform you of the current program line number. +7. Now let’s step into your recursive function to get a closer look at recursion. Let the program run until +it enters the first recursive function call: + +```console +continue +``` + +Inspect the data in the function arguments and the grid of booleans. Print the coordinates of the +current location, which are passed in as function arguments named x and y (for example). + +```console +print x +print y +``` + +If you are using grid2.txt as your grid, these values should read x = 9 and y = 7. +8. Let’s navigate within our recursive algorithm using step and next. NOTE: Though similar sounding, +these are two different commands in gdb/lldb. step will enter into the details of a called function and +allow you to walk through its code, while next will fully execute one line from the current function +(skipping over all of the details of the function as it’s executed). +Use the next command to move down until we hit our next recursive call. Once on this line, let’s step +into the function call. After you step into the function, you may want to type continue so we can skip +to the next breakpoint. Print out the coordinates of the current location, which should be different. +Also, let’s print the boolean values from locations in the grid adjacent to the current position. For +example: + +```console +print blocked_grid +print blocked_grid[x][y] +print blocked_grid[x-1][y+1] +``` +Because gdb supports using basic math on these variables, we can also print the grid values above, +below, to the left, and to the right of our current position. +9. Use step and continue to go further and further into the recursion. Use backtrace to show the +function calls currently on the call stack, including the specific line numbers. As you walk step by step +through your algorithm and print data, do the variable values and sequence of locations and function +calls match your expectations? Ask a TA or mentor if you have questions about this information. +10. Finally, delete all of the breakpoints and then let the program finish without interruption. + +```console +continue +``` + +**To complete this checkpoint and the entire lab**, present your modified program to a TA or mentor. +Demonstrate your skills with gdb/lldb to print out values of adjacent positions in the blocked grids vector, +to use backtrace, and navigate within recursive function calls using next, step, continue. Be prepared to +discuss how you will use gdb/lldb to help debug your future Data Structures homeworks. diff --git a/labs/08_recursion/grid1.txt b/labs/08_recursion/grid1.txt new file mode 100644 index 0000000..46fcad3 --- /dev/null +++ b/labs/08_recursion/grid1.txt @@ -0,0 +1,7 @@ +1 0 +2 1 2 3 +3 3 3 4 +5 5 + +0 0 +4 4 diff --git a/labs/08_recursion/grid2.txt b/labs/08_recursion/grid2.txt new file mode 100644 index 0000000..22f1824 --- /dev/null +++ b/labs/08_recursion/grid2.txt @@ -0,0 +1,11 @@ +2 0 3 1 +0 1 3 3 +1 2 4 1 5 2 +1 3 5 3 +0 5 2 5 +5 4 6 4 7 4 +3 5 4 6 5 6 7 5 +7 7 8 8 + +0 0 +9 7 diff --git a/labs/08_recursion/grid3.txt b/labs/08_recursion/grid3.txt new file mode 100644 index 0000000..74ca962 --- /dev/null +++ b/labs/08_recursion/grid3.txt @@ -0,0 +1,28 @@ +0 1 +2 2 +2 3 +3 3 +4 3 +3 1 +1 4 +5 5 +6 6 +1 9 +1 8 +1 7 +3 7 +2 7 +3 10 +7 7 +7 8 +7 6 +7 5 +7 4 +7 3 +8 3 +9 3 +10 3 + +0 0 +10 10 + diff --git a/labs/08_recursion/grid4.txt b/labs/08_recursion/grid4.txt new file mode 100644 index 0000000..4b27116 --- /dev/null +++ b/labs/08_recursion/grid4.txt @@ -0,0 +1,41 @@ +0 1 +2 2 +2 3 +3 3 +4 3 +3 1 +1 4 +5 5 +6 6 +1 9 +1 8 +1 7 +3 7 +2 7 +3 10 +7 7 +7 8 +7 6 +7 5 +7 4 +7 3 +8 3 +9 3 +10 3 +15 15 +17 15 +16 15 +18 15 +14 15 +13 15 +12 14 +17 18 +7 18 +7 17 +7 16 +7 15 +7 14 + +0 0 +18 18 + diff --git a/labs/08_recursion/start.cpp b/labs/08_recursion/start.cpp new file mode 100644 index 0000000..856009f --- /dev/null +++ b/labs/08_recursion/start.cpp @@ -0,0 +1,130 @@ +// Starting code for Checkpoints 2 and 3. This includes +// functions to read the grid and to output it. + +#include +#include +#include +#include + + +// A simple class to represent a point location. It only has a +// constructor and a two public member variables. This is one of the +// few times that you are allowed to use non-private member variables. + +class Point { +public: + Point(int x0, int y0) : x(x0), y(y0) {} + int x,y; +}; + + +// NOTE: We could use a boolean (true/false) to represent whether each +// cell of the grid was blocked or open. This would be the minimal +// representation for memory. + +// However, debuggers (both traditional and memory debuggers) might +// not be able to help debug errors with vectors of booleans, if they +// are efficiently packed by a clever STL implementation. So instead +// we use an enum, to improve readability, and to ensure that the +// status of each grid cell is stored as an integer avoiding debugger +// confusion during development. + + +enum GRID_STATUS { GRID_CLEAR, GRID_BLOCKED }; + + +// Input the grid and the start location. The input is a sequence of +// x y locations, terminated by x==0 and y==0. The last input, which +// follows 0 0 input, is the start location. +// +// The grid is represented as a 2d vector of GRID_STATUS, with each location +// that is blocked --- meaning that no path can go through --- being +// represented by the value GRID_BLOCKED. The grid is large enough to +// include all blocked points and include the starting location. The +// first coordinate of the vector of vectors is the x coordinate, and +// the second is the y coordinate. The format of the input is +// specified in the lab handout. + +void read_grid(std::istream& istr, + std::vector >& blocked_grid, + int& start_x, int& start_y) { + + // Read the x y locations into a list of Points. Keep track of the + // max x and max y values so that the size of the grid can be + // determined. + int x, y; + int max_x = 0, max_y = 0; // keep track of the max coordinate values + std::list blocked_points; + while ((istr >> x >> y) && ! (x==0 && y==0)) { + blocked_points.push_back(Point(x,y)); + if (x > max_x) max_x = x; + if (y > max_y) max_y = y; + } + + // Now that a 0 0 location has been read, read the start location. + // If this is beyond the max x or y value then update these values. + istr >> start_x >> start_y; + if (start_x > max_x) max_x = start_x; + if (start_y > max_y) max_y = start_y; + + // Make a vector of vectors with all entries marked clear. + std::vector one_row_of_ys(max_y+1, GRID_CLEAR); + std::vector > empty_grid(max_x+1, one_row_of_ys); + blocked_grid = empty_grid; + + // For Point in the list, mark the location in the list as blocked. + std::list::iterator p; + for (p = blocked_points.begin(); p != blocked_points.end(); ++p) { + blocked_grid[p->x][p->y] = GRID_BLOCKED; + } +} + + +// Output the grid to cout. The form of the output is explained in +// the cout statement below. + +void print_grid(const std::vector > & blocked_grid, + unsigned int start_x, unsigned int start_y) { + + std::cout << "Here is the grid with the origin in the upper left corner, x increasing \n" + << "horizontally and y increasing down the screen. An 'X' represents a blocked\n" + << "location and the 'S' represents the starting location.\n\n"; + + for (unsigned int y=0; y > blocked_grid; + int start_x, start_y; + read_grid(istr, blocked_grid, start_x, start_y); + print_grid(blocked_grid, start_x, start_y); + + // Start here with your code... + + + + return 0; +} +