CMSC160 Intro to Algorithmic Design Blaheta Lab 9: Pointers and arrays 3 Nov 2011 As promised, we ll spend today working on using pointers and arrays, leading up to a game you ll write that heavily involves arrays. But first: Vim FOTD: movement keys Vim responds to the arrow keys and keys like PageUp and PageDown, but there are a number of additional keys that can be pressed in command mode to move around the file. Open one of the files you have lying around and try some of them out. Key(s) Movement h Left one character j Down one line k Up one line l Right one character To beginning of current line $ To end of current line Ctrl-F Forward one page (screen) Ctrl-B Back one page (screen) G To last line of file #G To line # (e.g. 1G to go to top of file) w To beginning of next punctuation-delimited word W To beginning of next whitespace-delimited word e To end of this punctuation-delimited word E To end of this whitespace-delimited word b To beginning of this punctuation-delimited word B To beginning of this whitespace-delimited word To next (batch of) blank line(s) To previous (batch of) blank line(s) % To matching paren/bracket [( To previous unmatched left paren [m To start of current function Some of these are more mnemonic than others, of course. The first four are not mnemonic at all, but super-convenient once you ve got them in muscle
memory, because they re right in the home row, so your fingers don t have to go anywhere to type them. So what, right? Well, all of the delete commands that you learned in earlier labs were special cases of a rule: d plus a movement command deletes from here to wherever that movement goes. So, d1g deletes to the top of the file. And d% deletes everything between this paren and the matching one. Since the p command only pastes the most recently-deleted thing, it s very helpful to be able to delete everything you want to cut all at once. Same goes for the y ( yank ) commands. There s no need to memorise all the movement commands right now, of course. A couple might stick, but for the rest, even if you don t remember the command, you ll remember it exists, and you can always come back and refer to this sheet. A basic array program Like a vector, an array holds an arbitrary number of elements of a particular type; also like a vector, elements of an array are (usually) accessed using an index in square brackets, like this: values[i]. However, arrays are a more basic, low-level construct, and there are several features of vectors that arrays don t have. Our first program of the week will be similar to some of the programs and functions we ve written for vectors, mostly to show the places where arrays are different from vectors. Specifically, the program arraysum will let the user specify how many values they ll enter, and then type them in; after each number the entire array will be printed out, and at the end, the sum will be printed. So if the user types 3 (meaning three numbers will be entered), and then 5 and 44 and -7, the program will print back Contents: 5 0 0 Contents: 5 44 0 Contents: 5 44-7 Sum: 42 (Why the extra zeroes? We ll get to that.) Start the lab s readme file with this information and create your test cases; then create the skeleton of the cpp file to get started. 2
The first thing you ll do inside main is to read in the number of entries the array will eventually have. This is just as you ve done before, something like int n; cin >> n; The next line after that will have a new array-ish syntax, but more importantly it highlights the first big difference between vectors and arrays: Array size is set when you declare the array, and doesn t change That s why we needed to read the number first; we have to know how big the array will be before we can even declare it! The declaration will look like this: int values[n]; This is pretty similar to the square-bracket syntax for accessing string/vector/array elements, actually. The difference is that those occur in the middle of a value-producing expression, whereas this is on a line by itself, preceded by a type name like int (or string, or char, or whatever). If arrays achieve their full size immediately, what s contained in those n array positions? Array elements have no defined initial values (you must set them) If you re lucky, the element values may be zero already but maybe not. Depending on what happened to be in those bytes of memory before, the array could contain literally any value when it is declared. So we initialise the values to zero: for (int i = 0; i < n; ++i) values[i] = 0; // or use equivalent while loop A program that accesses elements of an array before assigning something to them is by definition incorrect, since you can t be sure what it will actually do. 3
At this point, save and compile your code just to check for typoes. In fact, you can even run it, although it won t do much yet. The next loop will actually read in the values. Other than the function call (about which more in a moment, this should be familiar: for (int i = 0; i < n; ++i) cin >> values[i]; cout << "Contents:"; print_array(values); // not quite right yet cout << endl; Now, what about that function? It s just what we d write if values were a vector, but it won t work here because unlike vectors, Arrays don t have a function to tell you their size In other words, if the only thing we passed into the function were the array, we would not be able to write a loop condition to know when to stop. (Notice that the loops we ve written here use n, which we know separately, rather than anything like values.size().) So the function call will have to be print_array (values, n); so that we can pass in both the array and its length. The corresponding function prototype will be void print_array (int[], int); and the function itself will start with a header like void print_array (int values[], int len) Notice that the size of the array is omitted from the square brackets in the prototype and header, because we don t know it. Type in the prototype at the top of the file, and then start a new file arrayfuncs.cpp to contain the print array function (and later the sum array function). Once you ve gotten past the header, the array-based print function will be nearly identical to the corresponding vector-based version: 4
for (int i = 0; i < len; ++i) cout << " " << values[i]; Once you ve got that function typed in, try compiling and running the program (don t forget to include both cpp files). Debug it carefully, and pay attention to any new error messages that you haven t seen before. Then, add the function sum array that computes and returns the sum of the values in a given array with a given size; and call that function in order to print out the sum. Experimenting with pointers and arrays The program for this portion of the lab won t really have an official purpose other than serving as a testing ground for you to see how pointers and arrays work. Start a file pointer test.cpp with the usual skeleton, and then type in the following to the body of main: int m = 42, n = 23; char str[4]; str[0] = B ; str[1] = a ; str[2] = r ; str[3] = 0; // <- not a typo int arr[2] = 99999, -99999; int *p = &m; char *cp = &str[1]; cout << "m: " << m << endl; cout << "n: " << n << endl; cout << "str: " << str << endl; cout << "arr: " << arr << endl; cout << "arr[0]: " << arr[0] << endl; cout << "arr[1]: " << arr[1] << endl; cout << "p: " << p << endl; cout << "cp: " << cp << endl; 5
First of all, compile it and run it, and see if you can understand why each of the cout lines prints what it does. Then, spend ten or fifteen minutes changing parts of the program to see if you can predict what those changes will do (and if you incorrectly predict them, to understand what they did). Most of your changes will go after the declarations and before the cout lines, but you re welcome to change other things too. Some suggestions: what happens if you assign to m after p has been initialised? What if you change one of the characters in str after cp has been initialised? What happens if you try to print the value of str[4]? Or str[-1]? What if you try assigning to those? What happens if you assign to *p? What does p[0] do? But don t stop there be creative! If you ve found something you can t explain, call me over and ask about it, or write it down and ask in class tomorrow. It s ok if the handed-in version of this program doesn t include every thing you experimented with, but if you find something unexpected, you might want to leave it in a comment rather than deleting it. Hunt the wumpus The longer project this week is to write a game called hunt the wumpus, where you wander in a cave trying to find and kill the wumpus 1 while collecting gold and not falling into a pit or getting eaten. Add the description and notes to your readme, although since the program will be both interactive and partially random, you won t be able to write diff-able test cases in the way we ve done in the past. Setup The core data element of this program will be a two-dimensional array. After you have put in the skeleton of the program, type the following at the top of main: char cave[5][4]; 1 A mythical computer science beast. Far cooler than dragons. 6
This creates a two-dimensional array of char that will let us keep track of what is actually in each location in a rectangular cave; it has five values for the x coordinate, ranging from 0 to 4, and four values for the y coordinate, from 0 to 3. Since arrays don t initialise themselves, we ll do that next: for (int x = 0; x < 5; ++x) for (int y = 0; y < 4; ++y) cave[x][y] =. ; We ll be using the period character to represent a cave location that is empty, so for every x value, for every y value, we store the empty marker in that location in the cave. In general, the syntax for accessing an element in a two-dimensional array is just like for regular arrays except you have to provide first one index (in square brackets), then the other index (also in square brackets). We also need to keep track of where in the cave the player is. int playerx = 0; int playery = 0; To run the game, we ll need a loop that reads the player s commands, responds to them, and prints the new prompt: cout << "> "; string command; while (cin >> command) if (command == "burp") cout << "Excuse you!" << endl; else cout << "I don t know how to do that." << endl; cout << "You are at (" << playerx << "," << playery << ")." << endl; cout << "> "; 7
You re still unable to move or do anything interesting in the still-totallyempty cave, but the general framework is there. Compile it and confirm that it runs. A randomly-placed wumpus, and other things We ll use the character W to represent the wumpus, and we ll place it at a random location in the cave. The random() function is built in, although you will have to #include <cstdlib> at the top of the file to enable it. It returns a random non-negative long which could be really large! but we can use it to generate small integers using the remainder operator. You can add the wumpus in one step with the following line (which should go after the cave is initialised but certainly before the command loop!): cave[random() % 5][random() % 4] = W ; You will also need to place at least one pit and at least one gold, although if you want to defer that until later that s ok; the general technique is the same, but then you start to have to worry about overwriting other placed things (like the wumpus!). The rest of the game Implementation of this game gets pretty open-ended pretty fast, but there are a few things you have to do and a short-list of relatively important features. You MUST implement: Commands north, south, east, and west, which move the player in their respective directions. Death (end of game) if the player moves to the location of the wumpus. A cheat command (which can be called cheat or can be something else) that prints out the map of the cave, including the wumpus and other contents. This will use a nested for (or while) loop similar to the one used to initialise the cave. Crucial for debugging. A message (after the location, before the prompt) indicating what is in the cave at the player s current location (e.g. gold, a pit). 8
You must implement AT LEAST THREE of: A command take that, if there is gold at the player s current location, takes it. This should both remove it from the cave location and increment a counter somewhere that says how much gold the player has found. That number should be printed when the game ends. Immobility: the movement commands don t succeed if the player has entered a location that is a pit. Commands shoot north, shoot east, etc, that let the player shoot at the wumpus, and if the wumpus is in the adjacent location in that direction, it dies and the player wins. Note that the value of command for these will be simply shoot, but if that s detected you can cin another word to find the direction the shot will go. Cave walls: if the player tries to walk in a direction that would take them outside the rectangle of the cave, print You can t go that way instead of whatever strange thing C++ would otherwise do. Perception: if the player is adjacent to the wumpus or to a pit, they should smell a stench or feel a breeze, respectively. (If they are adjacent to both, then they should perceive both a stench and a breeze.) Evasion: if the player shoots and doesn t hit the wumpus, the wumpus moves into an adjacent room. (If it runs into the player s room, the player dies.) Note that this requires you ve implemented shooting. And you MAY ALSO implement: Long-distance shooting: when you shoot in a direction, it continues in a straight line until it hits the edge of the cave, thus killing a wumpus in any of those locations. Bats: Some cave locations may have (very strong!) bats in them; if the player is in the same room as them, they may with some probability pick up the player and transport them to a random location in the cave before going back to their nest. Wandering wumpus: Every time the player does something, there is some low (10% or so) probability that the wumpus will move to an adjacent location. This movement will be audible throughout the cave (but of course its location will not be clear). 9
Whichever ones you do, and whatever order you do them in, make sure to do them one at a time, and when you get one working save a copy of your work before doing the next one, so you can go back to the earlier version if things go haywire. As you implement more features, be mindful of opportunities to move some of the work into separate functions, especially if you find yourself typing the same things in multiple places. (It won t always be possible, but where it is it s an improvement.) Handing in Hand it in as Lab 9 (or lab9 on torvalds). Aim to hand it in by class time on Wednesday, unless you have a specific question about something; if you come to me on Wednesday with questions you can hand in the fixed version by lab time Thursday. Extras Anything after three from the AT LEAST THREE or from the MAY ALSO list is an extra. See me first if you want to do something not on those lists. 10