1 <!DOCTYPE HTML PUBLIC
"-//W3O//DTD W3 HTML 2.0//EN">
4 <link rev=
"owner" href=
"mailto:scs@eskimo.com">
5 <link rev=
"made" href=
"mailto:scs@eskimo.com">
6 <title>Assignment #
5 Answers
</title>
9 <H1>Assignment #
5 Answers
</H1>
15 <B>Intermediate C Programming
18 UW Experimental College
21 <B>Assignment #
5 ANSWERS
25 <I>If you didn't use dynamically-allocated memory
26 to hold long object and room descriptions,
28 </I><p>See the published answer to
30 assignment
2, exercise
2.
32 <I>Rewrite
<TT>newobject
</TT>
34 to dynamically allocate new structures,
35 rather than parceling them out of
36 the static
<TT>objects
</TT> and
<TT>rooms
</TT> arrays.
37 </I><p>Rewriting
<TT>newobject
</TT> in
<TT>object.c
</TT>
45 objp = chkmalloc(sizeof(struct object));
47 strcpy(objp-
>name, name);
48 objp-
>lnext = NULL;
49 objp-
>attrs = NULL;
50 objp-
>contents = NULL;
56 <p>However, it isn't quite so simple for
<TT>newroom
</TT>,
57 because we need a way of looking through all the rooms
59 for example in the
<TT>findroom
</TT> function.
60 Therefore, we can't allocate room structures in isolation.
61 <p>As suggested in the assignment,
63 to keep a linked list of all rooms allocated,
65 an extra ``next'' field
66 to
<TT>struct room
</TT> in
<TT>game.h
</TT>:
71 struct object *contents;
72 struct room *exits[NEXITS];
73 char *desc; /* long description */
74 struct room *next; /* list of all rooms */
77 Now I can rewrite
<TT>newroom
</TT> in
<TT>rooms.c
</TT>:
79 static struct room *roomlist = NULL;
87 roomp = chkmalloc(sizeof(struct room));
89 roomp-
>next = roomlist; /* splice into list of all rooms */
92 strcpy(roomp-
>name, name);
93 roomp-
>contents = NULL;
94 for(i =
0; i
< NEXITS; i++)
95 roomp-
>exits[i] = NULL;
96 roomp-
>desc = NULL;
101 Splicing the new room into the list of all rooms is straightforward.
102 <p><TT>findroom
</TT> must be rewritten slightly,
103 to walk over the new room list rather than the old rooms array:
110 for(roomp = roomlist; roomp != NULL; roomp = roomp-
>next)
112 if(strcmp(roomp-
>name, name) ==
0)
119 <p>We also have to decide what to do with the
<TT>getentryroom
</TT> function.
120 (This is the function that gets called to decide
121 which room the actor should initially be placed in.)
124 the first room in the
<TT>rooms
</TT> array,
125 which always happened to correspond to the first room in the data file,
126 which was a reasonable choice.
127 If we make the obvious change to
<TT>getentryroom
</TT>,
128 and have it return the ``first'' room in the new room list:
133 return roomlist; /* temporary */
136 it will
<em>not
</em> be so reasonable a choice,
137 because our easy implementation of the list-splicing code
138 in
<TT>newroom
</TT> above
139 adds new rooms to the head of the list,
140 such that the first room added,
141 i.e. the first room in the data file,
142 ends up at the end of the list.
143 Depending on what your data file looks like,
144 the game with the modifications shown so far will start out
145 by dumping the player unceremoniously onto the back porch or into the basement
146 (which might actually end up being an advantage for the player,
147 if the basement tunnel is where the treasure is!).
149 you can rewrite
<TT>newroom
</TT> to place new rooms at the end of the list,
150 or
<TT>getentryroom
</TT> to return the tail of the list,
151 to solve this problem.
152 However, a better fix
153 would be to allow the data file
154 to explicitly specify what the entry room should be,
155 rather than making the game code assume anything.
156 We'll leave that for another exercise.
158 <I>Improve the code in
<TT>io.c
</TT>
159 so that room exit lists can be placed directly in the room descriptions,
160 rather than at the end.
161 </I><p>The basic change is to the
<TT>parsedatafile
</TT> function.
162 Whereas it used to parse lines at the end of the data file like
164 roomexits kitchen s:hall e:stairway n:porch
166 we'll now make it parse lines like
172 which will occur within the description of the particular room
173 (in this case, the kitchen)
175 (I've simplified things
176 by putting three exits on three separate lines
177 and eliminating the colon syntax;
178 there was no particular reason
179 for me to have done it in that more complicated way in the first place.)
180 <p>Here is the new code for
<TT>parsedatafile
</TT>:
182 else if(strcmp(av[
0],
"exit") ==
0)
187 fprintf(stderr,
"missing exit or room name\n");
190 if(currentroom == NULL)
192 fprintf(stderr,
"exit not in room\n");
195 roomp = findroom(av[
2]);
198 /* already have room, so connect */
199 connectexit(currentroom, av[
1], roomp);
202 /* haven't seen room yet; stash for later */
203 stashexit(currentroom, av[
1], av[
2]);
207 This makes use of two auxiliary functions:
210 connectexit(struct room *room, char *dirname, struct room *nextroom)
214 if(strcmp(dirname,
"n") ==
0)
216 else if(strcmp(dirname,
"e") ==
0)
218 else if(strcmp(dirname,
"w") ==
0)
220 else if(strcmp(dirname,
"s") ==
0)
222 else if(strcmp(dirname,
"ne") ==
0)
224 else if(strcmp(dirname,
"se") ==
0)
226 else if(strcmp(dirname,
"nw") ==
0)
228 else if(strcmp(dirname,
"sw") ==
0)
230 else if(strcmp(dirname,
"u") ==
0)
232 else if(strcmp(dirname,
"d") ==
0)
235 fprintf(stderr,
"no such direction \"%s\
"\n", dirname);
239 room-
>exits[dir] = nextroom;
247 struct stashedexit *next;
250 static struct stashedexit *stashedexits = NULL;
253 stashexit(struct room *room, char *dirname, char *nextroom)
255 struct stashedexit *ep = chkmalloc(sizeof(struct stashedexit));
257 ep-
>dirname = chkstrdup(dirname);
258 ep-
>nextroom = chkstrdup(nextroom);
259 ep-
>next = stashedexits;
263 The
<TT>stashexit
</TT> function builds a linked list of stashed exits,
264 allocating a new instance of the new
<TT>struct stashedexit
</TT>
266 each containing the room pointer
268 and the names of the exit direction and destination room.
269 (The strings must also be copied to dynamically-allocated memory,
270 because on entry to
<TT>stashexit
</TT>
271 they are pointers back into
272 the
<TT>line
</TT> or
<TT>line2
</TT> array in
<TT>parsedatafile
</TT>,
273 and those strings will be overwritten
274 when we read the next line in the data file.)
275 <p>Finally, we must arrange to resolve the stashed exits
276 when we're done reading the data file
277 and have had a chance to see the descriptions for all the rooms.
278 Here is the change to the
<TT>readdatafile
</TT> function:
280 static void connectexit(struct room *, char *, struct room *);
281 static void stashexit(struct room *, char *, char *);
282 static void resolveexits(void);
286 char *datfile =
"dungeon.dat";
287 FILE *fp = fopen(datfile,
"r");
290 fprintf(stderr,
"can't open %s\n", datfile);
300 <p>And here is the third auxiliary function:
305 struct stashedexit *ep, *nextep;
307 for(ep = stashedexits; ep != NULL; ep = nextep)
309 struct room *roomp = findroom(ep-
>nextroom);
311 fprintf(stderr,
"still no such room \"%s\
"\n", ep-
>nextroom);
312 else connectexit(ep-
>room, ep-
>dirname, roomp);
314 nextep = ep-
>next;
315 free(ep-
>dirname);
316 free(ep-
>nextroom);
323 One aspect of this last function deserves mention.
324 Why does it not use a more usual list-traversal loop,
327 for(ep = stashedexits; ep != NULL; ep = ep-
>next)
329 What's with that temporary variable
<TT>nextep
</TT>?
330 This loop is doing two things at once:
331 traversing the linked list
333 to resolve the stashed exits,
334 <em>and
</em> freeing the list as it goes.
335 But when it frees the node
336 (the
<TT>struct stashedexit
</TT>)
338 it also frees--and hence loses--the
339 <TT>next
</TT> pointer which that structure contains!
341 it makes a copy of the
<TT>next
</TT> pointer
342 <em>before
</em> it frees the structure.
346 This page by
<a href=
"http://www.eskimo.com/~scs/">Steve Summit
</a>
347 //
<a href=
"copyright.html">Copyright
</a> 1995-
9
348 //
<a href=
"mailto:scs@eskimo.com">mail feedback
</a>