Assignment #2

Intermediate C Programming

UW Experimental College


Assignment #2

Handouts:

Assignment #2
Assignment #1 Answers
Class Notes, Chapter 16
Class Notes, Chapter 17

Review Questions:

  1. List some differences between text and binary data files.

Exercises:

  1. We're going to fix one of the more glaring deficiencies of the initial implementation of the game, namely that the description of the dungeon takes the form of hard-compiled structure arrays in main.c, such that the source code must be edited (and in a tedious way, at that) and the game recompiled whenever we want to modify the dungeon. (We're used to recompiling when we make changes to our programs, but we shouldn't have to recompile when we make expected changes to our data. Imagine if a spreadsheet program required you to enter your worksheet expressions or data with a C compiler and recompile each time!)

    We're going to add code which can read the dungeon description dynamically, at run time, from a simple, textual data file. The file will describe the rooms, the objects and which rooms they're initially in, and the exits which interconnect the rooms. As the file is read, we'll dynamically assign new instances of struct room and struct object to define the rooms and objects which the data file describes. We won't know how many room and object structures we'll need, and we certainly won't know what descriptions they will contain, so we'll abandon the rooms and objects arrays from main.c, along with their cumbersome initializations. We'll add (in objects.c) an allocation function, newobject(), which will return a pointer to a brand-new object structure which we can use to contain some new object, usually one we've just read from the data file. Similarly, we'll add a newroom() function to rooms.c which will return a pointer to a brand-new room structure. (We're not quite ready to do full-blown dynamic memory allocation yet, so for now, the room and object allocation functions will dole structures out of fixed-size arrays which we'll arbitrarily declare as having size 100. Naturally we'll keep track of how many structures are actually in use, and complain if an attempt is made to use more than 100. Since the allocation code is isolated down within rooms.c and object.c, only those modules will need rewriting when we move to a more flexible allocation scheme; the code which calls newroom() and newobject() won't need to change.)

    Appended to this assignment is a listing of a new file, io.c, for doing the data file reading. (This code is also on the disk or at the ftp site, in the week2 subdirectory.)

    Rip out the definitions and initializations of the objects and rooms arrays at the top of main.c; also remove the initialization of actor (but leave the definition). At the top of main(), before the initial call to listroom, add the lines
    	if(!readdatafile())
    		exit(1);
    
    	gotoroom(&actor, getentryroom());	/* put actor in initial room */
    


    Add the following code to object.c (it can go near the top):
    #define MAXOBJECTS 100
    
    static struct object objects[MAXOBJECTS];
    static int nobjects = 0;
    
    struct object *
    newobject(char *name)
    {
    struct object *objp;
    
    if(nobjects >= MAXOBJECTS)
    	{
    	fprintf(stderr, "too many objects\n");
    	exit(1);
    	}
    
    objp = &objects[nobjects++];
    
    strcpy(objp->name, name);
    objp->lnext = NULL;
    
    return objp;
    }
    
    (This code is in the file object.xc in the week2 subdirectory.)

    Add the following code to rooms.c (it can go near the top):
    #define MAXROOMS 100
    
    static struct room rooms[MAXROOMS];
    static int nrooms = 0;
    
    struct room *
    newroom(char *name)
    {
    struct room *roomp;
    int i;
    
    if(nrooms >= MAXROOMS)
    	{
    	fprintf(stderr, "too many rooms\n");
    	exit(1);
    	}
    
    roomp = &rooms[nrooms++];
    
    strcpy(roomp->name, name);
    roomp->contents = NULL;
    for(i = 0; i < NEXITS; i++)
    	roomp->exits[i] = NULL;
    
    return roomp;
    }
    
    struct room *
    findroom(char *name)
    {
    int i;
    
    for(i = 0; i < nrooms; i++)
    	{
    	if(strcmp(rooms[i].name, name) == 0)
    		return &rooms[i];
    	}
    
    return NULL;
    }
    
    struct room *
    getentryroom(void)
    {
    if(nrooms == 0)
    	return NULL;
    return &rooms[0];	/* temporary */
    }
    
    (This code is in the file rooms.xc in the week2 subdirectory.)

    You'll also need the file fgetline.c, which is appended behind the new io.c, and also in the week2 subdirectory.

    After adding all the new code, and any necessary prototype declarations to game.h, the program should again compile and run, but it will expect to find a data file named dungeon.dat in the current directory. A sample dungeon.dat file (corresponding to the old, hard-compiled game) is included here (and in the week2 subdirectory). Once you see how it works, you can start extending the dungeon simply by modifying the dungeon.dat file.
  2. (tricky) If you added long descriptions to rooms and objects last week, devise and implement a way for those long descriptions to be read from the data file.
  3. (easier) Expand the set of allowable exits from rooms. Add the possibility of northeast, southeast, northwest, and southwest exits, or ``up'' and ``down'' exits, or all six. Add commands to go in these new directions. Modify the data file reading code in io.c to handle setting up the new exits.


This page by Steve Summit // Copyright 1995-9 // mail feedback