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 #
4 Answers
</title>
9 <H1>Assignment #
4 Answers
</H1>
15 <B>Intermediate C Programming
18 UW Experimental College
21 <B>Assignment #
4 ANSWERS
25 <I>Modify the
<TT>findobject
</TT> function in
<TT>object.c
</TT>
26 so that it can find objects when they're inside containers.
27 </I><p>As suggested in the assignment,
28 we'll need to break the old
<TT>findobject
</TT> function up
30 <TT>findobject
</TT> and
<TT>findobj2
</TT>.
31 <TT>findobject
</TT> accepts an actor and the name of an object to find
33 <TT>findobj2
</TT> accepts a pointer to a list of objects,
34 and the name of an object to find.
36 static struct object *findobj2(struct object *, char *);
39 findobject(struct actor *actp, char *name)
43 /* first look in actor's possessions: */
45 lp = findobj2(actp-
>contents, name);
49 /* now look in surrounding room: */
51 if(actp-
>location != NULL)
53 lp = findobj2(actp-
>location-
>contents, name);
63 /* find a named object in an object list */
64 /* (return NULL if not found) */
66 static struct object *
67 findobj2(struct object *list, char *name)
71 for(lp = list; lp != NULL; lp = lp-
>lnext)
73 if(strcmp(lp-
>name, name) ==
0)
75 if(lp-
>contents != NULL)
77 struct object *lp2 = findobj2(lp-
>contents, name);
88 The body of
<TT>findobj2
</TT>
89 is the loop that
<TT>findobject
</TT> used to use
90 when it searched the player's possessions and the room's contents;
91 <TT>findobject
</TT> now simply calls
<TT>findobj2
</TT> at those two spots.
92 <TT>findobj2
</TT> also calls itself, recursively,
93 when one of the objects it's inspecting
96 is a container object with its own contents.
97 <p>I've declared
<TT>findobj2
</TT> as
<TT>static
</TT>,
98 which means that it can only be called from within
<TT>object.c
</TT>.
99 It's an auxiliary function,
100 which only
<TT>findobject
</TT> calls,
101 so no one outside needs to (or should) be able to call it.
102 The function prototype for
<TT>findobj2
</TT>
103 includes the keyword
<TT>static
</TT>, too.
105 <I>Modify the
<TT>takeobject
</TT> function in
<TT>object.c
</TT>
106 so that it can also take objects which are sitting in containers.
107 </I><p>This is harder than it ought to be,
108 because we have to remove the object from the container's list;
110 it's not trivial to find the object which contains another object.
111 Ideally, all objects would also have pointers back to their containers
112 (just as actors already have pointers back to their rooms).
113 For now, we'll write a
<TT>findcontainer
</TT> function
114 which is kind of like
<TT>findobject
</TT>
115 except that it returns the object's container.
116 As for
<TT>findobject
</TT>,
117 we'll also need a recursive
<TT>findcont2
</TT>
123 ``didn't find it'' comment
125 <TT>return FALSE;
</TT>
128 /* perhaps it's in a container */
130 containerp = findcontainer(objp, actp, roomp);
132 if(containerp != NULL)
134 /* this test should probably be up in commands.c) */
136 if((containerp-
>attrs
& CLOSABLE)
&& !Isopen(containerp))
138 printf(
"The %s is closed.\n", containerp-
>name);
142 /* re-find in container's list, for splicing */
146 for(lp = containerp-
>contents; lp != NULL; lp = lp-
>lnext)
148 if(lp == objp) /* found it */
150 /* splice out of room's list */
152 if(lp == containerp-
>contents) /* head of list */
153 containerp-
>contents = lp-
>lnext;
154 else prevlp-
>lnext = lp-
>lnext;
156 /* splice into actor's list */
158 lp-
>lnext = actp-
>contents;
159 actp-
>contents = lp;
168 As the comment indicates,
169 the test for a closed container probably belongs up in
<TT>commands.c
</TT>.
170 (That's where the other similar tests are;
171 the functions in
<TT>object.c
</TT> are supposed to be low-level,
172 and just manipulate data structures.)
173 But, because of the clumsy way we're currently handling actors,
174 rooms, objects, and containers, it's hard to do it right.
175 (You may not yet have realized the clumsiness of the current scheme.
176 The problem is that actors are really objects
177 and rooms are really containers,
178 and since containers are objects,
179 rooms are objects, too.
180 We'll have more to say about this issue later.)
181 <p>Here are
<TT>findcontainer
</TT> and
<TT>findcont2
</TT>:
183 /* find an object's container (in actor's possesion, or room) */
184 /* (return NULL if not found) */
186 static struct object *
187 findcontainer(struct object *objp, struct actor *actp, struct room *roomp)
189 struct object *lp, *lp2;
191 /* first look in possessions: */
193 for(lp = actp-
>contents; lp != NULL; lp = lp-
>lnext)
195 if(lp-
>contents != NULL)
197 lp2 = findcont2(objp, lp);
203 /* now look in room: */
205 for(lp = roomp-
>contents; lp != NULL; lp = lp-
>lnext)
207 if(lp-
>contents != NULL)
209 lp2 = findcont2(objp, lp);
218 static struct object *
219 findcont2(struct object *objp, struct object *container)
223 for(lp = container-
>contents; lp != NULL; lp = lp-
>lnext)
227 if(lp-
>contents != NULL)
229 struct object *lp2 = findcont2(objp, lp);
239 <I>Rewrite the ``examine'' command
240 to mention some of the relevant attributes
241 of the object being examined.
</I>
242 <p>Here is the modified code
243 (from
<TT>docommand
</TT> in
<TT>commands.c
</TT>):
245 else if(strcmp(verb,
"examine") ==
0)
247 int printedsomething;
251 printf(
"You must tell me what to examine.\n");
255 printedsomething = FALSE;
257 if(objp-
>desc != NULL
&& *objp-
>desc != '\
0')
259 printf(
"%s\n", objp-
>desc);
260 printedsomething = TRUE;
263 if(Iscontainer(objp))
265 if(objp-
>attrs
& CLOSABLE)
267 printf(
"The %s is %s.\n", objp-
>name,
268 Isopen(objp) ?
"open" :
"closed");
269 printedsomething = TRUE;
272 if((!(objp-
>attrs
& CLOSABLE) || Isopen(objp))
&&
273 objp-
>contents != NULL)
275 printf(
"The %s contains:\n", objp-
>name);
276 listobjects(objp-
>contents);
277 printedsomething = TRUE;
281 if(objp-
>attrs
& BROKEN)
283 printf(
"The %s is broken.\n", objp-
>name);
284 printedsomething = TRUE;
287 if(objp-
>attrs
& SHARP)
289 printf(
"The %s is quite sharp.\n", objp-
>name);
290 printedsomething = TRUE;
293 if(!printedsomething)
294 printf(
"You see nothing special about the %s.\n", objp-
>name);
297 There are several conditions
298 under which this code prints something ``special''
299 about the object being examined:
300 if the object has a long description,
301 if the object is a container,
302 if the object has certain attributes,
304 Whenever the code prints one of these messages
305 (i.e. under any circumstances)
306 it sets the Boolean variable
<TT>printedsomething
</TT> to
<TT>TRUE
</TT>.
308 if we haven't found anything interesting to print
309 (i.e. if
<TT>printedsomething
</TT> is still
<TT>FALSE
</TT>),
310 we fall back on the generic
311 ``You see nothing special''
314 it's nice to arrange conditionals
315 so that you don't need extra Boolean variables.
317 the condition under which we print
318 ``You see nothing special''
319 would be so complicated
320 if we tried to express it directly
321 that it's much easier to just use the little
322 <TT>printedsomething
</TT> variable.
323 (Among other things, using
<TT>printedsomething
</TT>
324 means that it will be easier to add more attribute printouts later,
325 as long as they all set
<TT>printedsomething
</TT>, too.)
327 <I>Modify the
<TT>listobjects
</TT> function in
<TT>object.c
</TT>
328 to list the contents of objects which are containers.
329 </I><p>Here is the simple, straightforward implementation:
332 listobjects(struct object *list)
336 for(lp = list; lp != NULL; lp = lp-
>lnext)
338 printf(
"%s\n", lp-
>name);
339 if(lp-
>contents != NULL)
341 printf(
"The %s contains:\n", lp-
>name);
342 listobjects(lp-
>contents);
347 <p>However, this won't look too good,
348 because when we list, say, the contents of a room,
349 and the room contains a container and some other objects,
350 the list of objects in the container won't be demarcated from
351 the other objects in the room.
352 So, a fancier solution would be to indent each container's list
353 relative to the surrounding list.
354 To do this, we add a ``depth'' parameter
356 (with each recursive call)
357 of how deeply the objects and containers we're listing are.
358 The depth obviously starts out as
0,
359 and to keep all the old callers of
<TT>listobjects
</TT>
360 from having to add this new argument,
361 we rename
<TT>listobjects
</TT> as
<TT>listobjs2
</TT>,
362 and then have a new, stub version of
<TT>listobjects
</TT>
363 (which everyone else continues to call)
364 which simply calls
<TT>listobjs2
</TT>,
365 starting off the recursive chain at a depth of
0.
366 (Since
<TT>listobjs2
</TT> is only called by
<TT>listobjects
</TT>,
367 it's declared
<TT>static
</TT>.)
369 static void listobjs2(struct object *, int);
372 listobjects(struct object *list)
378 listobjs2(struct object *list, int depth)
382 for(lp = list; lp != NULL; lp = lp-
>lnext)
384 printf(
"%*s%s\n", depth,
"", lp-
>name);
385 if(lp-
>contents != NULL)
387 printf(
"%*sThe %s contains:\n", depth,
"", lp-
>name);
388 listobjs2(lp-
>contents, depth +
1);
393 The indentation is done
396 the
<TT>"%*s"</TT> format means to print a string
397 in a field of a given width,
398 where the width is taken from
<TT>printf
</TT>'s argument list.
399 The string we ask to be printed is the empty string,
400 because all we want is a certain number of spaces
401 (that is, the spaces which
<TT>printf
</TT> will add
402 to pad our string out to the requested width).
404 (once we know the trick, anyway)
405 this is considerably easier
406 than having to write little loops which print a certain number of spaces.
408 <I>Implement objects which can be locked.
409 </I><p>Here are the new ``lock'' and ``unlock'' commands:
411 else if(strcmp(verb,
"lock") ==
0)
415 printf(
"You must tell me what to lock.\n");
418 if(!(objp-
>attrs
& LOCK))
420 printf(
"You can't lock the %s.\n", objp-
>name);
425 printf(
"The %s is open.\n", objp-
>name);
428 if(cmd-
>preposition == NULL || strcmp(cmd-
>preposition,
"with") !=
0 ||
429 cmd-
>xobject == NULL)
431 printf(
"You must tell me what to lock with.\n");
434 if(!contains(player-
>contents, cmd-
>xobject))
436 printf(
"You have no %s.\n", cmd-
>xobject-
>name);
439 if(!(cmd-
>xobject-
>attrs
& KEY))
441 printf(
"The %s won't lock the %s.\n",
442 cmd-
>xobject-
>name, objp-
>name);
445 if(objp-
>attrs
& LOCKED)
447 printf(
"The %s is already locked.\n", objp-
>name);
451 objp-
>attrs |= LOCKED;
452 printf(
"The %s is now locked.\n", objp-
>name);
455 else if(strcmp(verb,
"unlock") ==
0)
459 printf(
"You must tell me what to unlock.\n");
462 if(!(objp-
>attrs
& LOCK))
464 printf(
"You can't unlock the %s.\n", objp-
>name);
467 if(cmd-
>preposition == NULL || strcmp(cmd-
>preposition,
"with") !=
0 ||
468 cmd-
>xobject == NULL)
470 printf(
"You must tell me what to unlock with.\n");
473 if(!contains(player-
>contents, cmd-
>xobject))
475 printf(
"You have no %s.\n", cmd-
>xobject-
>name);
478 if(!(cmd-
>xobject-
>attrs
& KEY))
480 printf(
"The %s won't unlock the %s.\n",
481 cmd-
>xobject-
>name, objp-
>name);
484 if(!(objp-
>attrs
& LOCKED))
486 printf(
"The %s is already unlocked.\n", objp-
>name);
490 objp-
>attrs
&= ~LOCKED;
491 printf(
"The %s is now unlocked.\n", objp-
>name);
494 <p>Here is the modified ``open'' command:
496 else if(strcmp(verb,
"open") ==
0)
500 printf(
"You must tell me what to open.\n");
505 printf(
"The %s is already open.\n", objp-
>name);
508 if(!(objp-
>attrs
& CLOSABLE))
510 printf(
"You can't open the %s.\n", objp-
>name);
513 if((objp-
>attrs
& LOCK)
&& (objp-
>attrs
& LOCKED))
515 printf(
"The %s is locked.\n", objp-
>name);
518 objp-
>attrs |= OPEN;
519 printf(
"The %s is now open.\n", objp-
>name);
522 (The ``close'' command doesn't need modifying;
523 we'll let open containers and doors swing and latch closed
524 even if they were already locked.)
525 <p>Finally, here are a few more lines for
<TT>io.c
</TT>,
526 to read the new attributes needed by
527 this and the following two exercises:
529 else if(strcmp(av[
1],
"lock") ==
0)
530 currentobject-
>attrs |= LOCK;
531 else if(strcmp(av[
1],
"locked") ==
0)
532 currentobject-
>attrs |= LOCKED;
533 else if(strcmp(av[
1],
"key") ==
0)
534 currentobject-
>attrs |= KEY;
535 else if(strcmp(av[
1],
"tool") ==
0)
536 currentobject-
>attrs |= TOOL;
537 else if(strcmp(av[
1],
"immobile") ==
0)
538 currentobject-
>attrs |= IMMOBILE;
541 <I>Implement a ``fix'' command
542 which will let the user fix broken objects.
543 </I><p>Here is the code
544 (for
<TT>docommand
</TT> in
<TT>commands.c
</TT>, of course):
546 else if(strcmp(verb,
"fix") ==
0)
550 printf(
"You must tell me what to fix.\n");
553 if(!(objp-
>attrs
& BROKEN))
555 printf(
"The %s is not broken.\n", objp-
>name);
558 if(cmd-
>preposition == NULL || strcmp(cmd-
>preposition,
"with") !=
0 ||
559 cmd-
>xobject == NULL)
561 printf(
"You must tell me what to fix with.\n");
564 if(!contains(player-
>contents, cmd-
>xobject))
566 printf(
"You have no %s.\n", cmd-
>xobject-
>name);
569 if(!(cmd-
>xobject-
>attrs
& TOOL))
571 printf(
"I don't see how you can fix things with a %s.\n",
572 cmd-
>xobject-
>name);
576 objp-
>attrs
&= ~BROKEN;
577 printf(
"Somehow you manage to fix the %s with the %s.\n",
578 objp-
>name, cmd-
>xobject-
>name);
582 <I>Implement immobile objects that can't be picked up.
583 </I><p>Here is the modified ``take'' command:
585 else if(strcmp(verb,
"take") ==
0)
589 printf(
"You must tell me what to take.\n");
592 if(contains(player-
>contents, objp))
594 printf(
"You already have the %s.\n", objp-
>name);
597 if(objp-
>attrs
& IMMOBILE)
599 printf(
"The %s cannot be picked up.\n", objp-
>name);
602 if(!takeobject(player, objp))
604 /* shouldn't happen */
605 printf(
"You can't pick up the %s.\n", objp-
>name);
613 This page by
<a href=
"http://www.eskimo.com/~scs/">Steve Summit
</a>
614 //
<a href=
"copyright.html">Copyright
</a> 1995-
9
615 //
<a href=
"mailto:scs@eskimo.com">mail feedback
</a>