* better
[mascara-docs.git] / lang / C / the.ansi.c.programming.language / notes.accompany.ansi.c / homework / PS4a.html
blob9d47e1c3c24cd9f34f1f54eb3f692c3d6ff1afd2
1 <!DOCTYPE HTML PUBLIC "-//W3O//DTD W3 HTML 2.0//EN">
2 <html>
3 <head>
4 <link rev="owner" href="mailto:scs@eskimo.com">
5 <link rev="made" href="mailto:scs@eskimo.com">
6 <title>Assignment #4 Answers</title>
7 </head>
8 <body>
9 <H1>Assignment #4 Answers</H1>
15 <B>Intermediate C Programming
16 <br>
17 <br>
18 UW Experimental College
19 </B><br>
20 <br>
21 <B>Assignment #4 ANSWERS
22 </B><br>
23 <br>
24 <p>Exercise 2.
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
29 into two functions:
30 <TT>findobject</TT> and <TT>findobj2</TT>.
31 <TT>findobject</TT> accepts an actor and the name of an object to find
32 (as before);
33 <TT>findobj2</TT> accepts a pointer to a list of objects,
34 and the name of an object to find.
35 <pre>
36 static struct object *findobj2(struct object *, char *);
38 struct object *
39 findobject(struct actor *actp, char *name)
41 struct object *lp;
43 /* first look in actor's possessions: */
45 lp = findobj2(actp-&gt;contents, name);
46 if(lp != NULL)
47 return lp;
49 /* now look in surrounding room: */
51 if(actp-&gt;location != NULL)
53 lp = findobj2(actp-&gt;location-&gt;contents, name);
54 if(lp != NULL)
55 return lp;
58 /* didn't find it */
60 return NULL;
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)
69 struct object *lp;
71 for(lp = list; lp != NULL; lp = lp-&gt;lnext)
73 if(strcmp(lp-&gt;name, name) == 0)
74 return lp;
75 if(lp-&gt;contents != NULL)
77 struct object *lp2 = findobj2(lp-&gt;contents, name);
78 if(lp2 != NULL)
79 return lp2;
83 /* didn't find it */
85 return NULL;
87 </pre>
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
94 (from either list,
95 or any list)
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.
104 </p><p>Exercise 3.
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;
109 however,
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>
118 function.
119 <p>I added this code
120 at the end of
121 <TT>takeobject</TT>
122 (just before the
123 ``didn't find it'' comment
124 and the last
125 <TT>return FALSE;</TT>
126 line):
127 <pre>
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-&gt;attrs &amp; CLOSABLE) &amp;&amp; !Isopen(containerp))
138 printf("The %s is closed.\n", containerp-&gt;name);
139 return FALSE;
142 /* re-find in container's list, for splicing */
144 prevlp = NULL;
146 for(lp = containerp-&gt;contents; lp != NULL; lp = lp-&gt;lnext)
148 if(lp == objp) /* found it */
150 /* splice out of room's list */
152 if(lp == containerp-&gt;contents) /* head of list */
153 containerp-&gt;contents = lp-&gt;lnext;
154 else prevlp-&gt;lnext = lp-&gt;lnext;
156 /* splice into actor's list */
158 lp-&gt;lnext = actp-&gt;contents;
159 actp-&gt;contents = lp;
161 return TRUE;
164 prevlp = lp;
167 </pre>
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>:
182 <pre>
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-&gt;contents; lp != NULL; lp = lp-&gt;lnext)
195 if(lp-&gt;contents != NULL)
197 lp2 = findcont2(objp, lp);
198 if(lp2 != NULL)
199 return lp2;
203 /* now look in room: */
205 for(lp = roomp-&gt;contents; lp != NULL; lp = lp-&gt;lnext)
207 if(lp-&gt;contents != NULL)
209 lp2 = findcont2(objp, lp);
210 if(lp2 != NULL)
211 return lp2;
215 return NULL;
218 static struct object *
219 findcont2(struct object *objp, struct object *container)
221 struct object *lp;
223 for(lp = container-&gt;contents; lp != NULL; lp = lp-&gt;lnext)
225 if(lp == objp)
226 return container;
227 if(lp-&gt;contents != NULL)
229 struct object *lp2 = findcont2(objp, lp);
230 if(lp2 != NULL)
231 return lp2;
235 return NULL;
237 </pre>
238 <p>Exercise 4.
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>):
244 <pre>
245 else if(strcmp(verb, "examine") == 0)
247 int printedsomething;
249 if(objp == NULL)
251 printf("You must tell me what to examine.\n");
252 return FALSE;
255 printedsomething = FALSE;
257 if(objp-&gt;desc != NULL &amp;&amp; *objp-&gt;desc != '\0')
259 printf("%s\n", objp-&gt;desc);
260 printedsomething = TRUE;
263 if(Iscontainer(objp))
265 if(objp-&gt;attrs &amp; CLOSABLE)
267 printf("The %s is %s.\n", objp-&gt;name,
268 Isopen(objp) ? "open" : "closed");
269 printedsomething = TRUE;
272 if((!(objp-&gt;attrs &amp; CLOSABLE) || Isopen(objp)) &amp;&amp;
273 objp-&gt;contents != NULL)
275 printf("The %s contains:\n", objp-&gt;name);
276 listobjects(objp-&gt;contents);
277 printedsomething = TRUE;
281 if(objp-&gt;attrs &amp; BROKEN)
283 printf("The %s is broken.\n", objp-&gt;name);
284 printedsomething = TRUE;
287 if(objp-&gt;attrs &amp; SHARP)
289 printf("The %s is quite sharp.\n", objp-&gt;name);
290 printedsomething = TRUE;
293 if(!printedsomething)
294 printf("You see nothing special about the %s.\n", objp-&gt;name);
296 </pre>
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,
303 etc.
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>.
307 At the very end,
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''
312 message.
313 <p>In simpler cases,
314 it's nice to arrange conditionals
315 so that you don't need extra Boolean variables.
316 Here, however,
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.)
326 <p>Exercise 5.
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:
330 <pre>
331 void
332 listobjects(struct object *list)
334 struct object *lp;
336 for(lp = list; lp != NULL; lp = lp-&gt;lnext)
338 printf("%s\n", lp-&gt;name);
339 if(lp-&gt;contents != NULL)
341 printf("The %s contains:\n", lp-&gt;name);
342 listobjects(lp-&gt;contents);
346 </pre>
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
355 which keeps track
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>.)
368 <pre>
369 static void listobjs2(struct object *, int);
371 void
372 listobjects(struct object *list)
374 listobjs2(list, 0);
377 static void
378 listobjs2(struct object *list, int depth)
380 struct object *lp;
382 for(lp = list; lp != NULL; lp = lp-&gt;lnext)
384 printf("%*s%s\n", depth, "", lp-&gt;name);
385 if(lp-&gt;contents != NULL)
387 printf("%*sThe %s contains:\n", depth, "", lp-&gt;name);
388 listobjs2(lp-&gt;contents, depth + 1);
392 </pre>
393 The indentation is done
394 using a
395 trick:
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.
407 <p>Exercise 6.
408 <I>Implement objects which can be locked.
409 </I><p>Here are the new ``lock'' and ``unlock'' commands:
410 <pre>
411 else if(strcmp(verb, "lock") == 0)
413 if(objp == NULL)
415 printf("You must tell me what to lock.\n");
416 return FALSE;
418 if(!(objp-&gt;attrs &amp; LOCK))
420 printf("You can't lock the %s.\n", objp-&gt;name);
421 return FALSE;
423 if(Isopen(objp))
425 printf("The %s is open.\n", objp-&gt;name);
426 return FALSE;
428 if(cmd-&gt;preposition == NULL || strcmp(cmd-&gt;preposition, "with") != 0 ||
429 cmd-&gt;xobject == NULL)
431 printf("You must tell me what to lock with.\n");
432 return FALSE;
434 if(!contains(player-&gt;contents, cmd-&gt;xobject))
436 printf("You have no %s.\n", cmd-&gt;xobject-&gt;name);
437 return FALSE;
439 if(!(cmd-&gt;xobject-&gt;attrs &amp; KEY))
441 printf("The %s won't lock the %s.\n",
442 cmd-&gt;xobject-&gt;name, objp-&gt;name);
443 return FALSE;
445 if(objp-&gt;attrs &amp; LOCKED)
447 printf("The %s is already locked.\n", objp-&gt;name);
448 return FALSE;
451 objp-&gt;attrs |= LOCKED;
452 printf("The %s is now locked.\n", objp-&gt;name);
455 else if(strcmp(verb, "unlock") == 0)
457 if(objp == NULL)
459 printf("You must tell me what to unlock.\n");
460 return FALSE;
462 if(!(objp-&gt;attrs &amp; LOCK))
464 printf("You can't unlock the %s.\n", objp-&gt;name);
465 return FALSE;
467 if(cmd-&gt;preposition == NULL || strcmp(cmd-&gt;preposition, "with") != 0 ||
468 cmd-&gt;xobject == NULL)
470 printf("You must tell me what to unlock with.\n");
471 return FALSE;
473 if(!contains(player-&gt;contents, cmd-&gt;xobject))
475 printf("You have no %s.\n", cmd-&gt;xobject-&gt;name);
476 return FALSE;
478 if(!(cmd-&gt;xobject-&gt;attrs &amp; KEY))
480 printf("The %s won't unlock the %s.\n",
481 cmd-&gt;xobject-&gt;name, objp-&gt;name);
482 return FALSE;
484 if(!(objp-&gt;attrs &amp; LOCKED))
486 printf("The %s is already unlocked.\n", objp-&gt;name);
487 return FALSE;
490 objp-&gt;attrs &amp;= ~LOCKED;
491 printf("The %s is now unlocked.\n", objp-&gt;name);
493 </pre>
494 <p>Here is the modified ``open'' command:
495 <pre>
496 else if(strcmp(verb, "open") == 0)
498 if(objp == NULL)
500 printf("You must tell me what to open.\n");
501 return FALSE;
503 if(Isopen(objp))
505 printf("The %s is already open.\n", objp-&gt;name);
506 return FALSE;
508 if(!(objp-&gt;attrs &amp; CLOSABLE))
510 printf("You can't open the %s.\n", objp-&gt;name);
511 return FALSE;
513 if((objp-&gt;attrs &amp; LOCK) &amp;&amp; (objp-&gt;attrs &amp; LOCKED))
515 printf("The %s is locked.\n", objp-&gt;name);
516 return FALSE;
518 objp-&gt;attrs |= OPEN;
519 printf("The %s is now open.\n", objp-&gt;name);
521 </pre>
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:
528 <pre>
529 else if(strcmp(av[1], "lock") == 0)
530 currentobject-&gt;attrs |= LOCK;
531 else if(strcmp(av[1], "locked") == 0)
532 currentobject-&gt;attrs |= LOCKED;
533 else if(strcmp(av[1], "key") == 0)
534 currentobject-&gt;attrs |= KEY;
535 else if(strcmp(av[1], "tool") == 0)
536 currentobject-&gt;attrs |= TOOL;
537 else if(strcmp(av[1], "immobile") == 0)
538 currentobject-&gt;attrs |= IMMOBILE;
539 </pre>
540 <p>Exercise 7.
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):
545 <pre>
546 else if(strcmp(verb, "fix") == 0)
548 if(objp == NULL)
550 printf("You must tell me what to fix.\n");
551 return FALSE;
553 if(!(objp-&gt;attrs &amp; BROKEN))
555 printf("The %s is not broken.\n", objp-&gt;name);
556 return FALSE;
558 if(cmd-&gt;preposition == NULL || strcmp(cmd-&gt;preposition, "with") != 0 ||
559 cmd-&gt;xobject == NULL)
561 printf("You must tell me what to fix with.\n");
562 return FALSE;
564 if(!contains(player-&gt;contents, cmd-&gt;xobject))
566 printf("You have no %s.\n", cmd-&gt;xobject-&gt;name);
567 return FALSE;
569 if(!(cmd-&gt;xobject-&gt;attrs &amp; TOOL))
571 printf("I don't see how you can fix things with a %s.\n",
572 cmd-&gt;xobject-&gt;name);
573 return FALSE;
576 objp-&gt;attrs &amp;= ~BROKEN;
577 printf("Somehow you manage to fix the %s with the %s.\n",
578 objp-&gt;name, cmd-&gt;xobject-&gt;name);
580 </pre>
581 <p>Exercise 8.
582 <I>Implement immobile objects that can't be picked up.
583 </I><p>Here is the modified ``take'' command:
584 <pre>
585 else if(strcmp(verb, "take") == 0)
587 if(objp == NULL)
589 printf("You must tell me what to take.\n");
590 return FALSE;
592 if(contains(player-&gt;contents, objp))
594 printf("You already have the %s.\n", objp-&gt;name);
595 return FALSE;
597 if(objp-&gt;attrs &amp; IMMOBILE)
599 printf("The %s cannot be picked up.\n", objp-&gt;name);
600 return FALSE;
602 if(!takeobject(player, objp))
604 /* shouldn't happen */
605 printf("You can't pick up the %s.\n", objp-&gt;name);
606 return FALSE;
608 printf("Taken.\n");
609 </pre>
610 <hr>
611 <hr>
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>
616 </p>
617 </body>
618 </html>