* remove "\r" nonsense
[mascara-docs.git] / C / the.ansi.c.programming.language / notes.accompany.ansi.c / homework / PS4.html
blob043a1a99831ae7ad1fac999597aa4d7a7f3402e4
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</title>
7 </head>
8 <body>
9 <H1>Assignment #4</H1>
15 <B>Intermediate C Programming
16 <br>
17 <br>
18 UW Experimental College
19 </B><br>
20 <br>
21 <B>Assignment #4
22 </B><p><B>Handouts:
23 </B></p><p><a href="PS4.html">Assignment #4</a>
24 <br><a href="PS3a.html">Assignment #3 Answers</a>
25 <br><a href="http://www.eskimo.com/~scs/cclass/int/sx6.html">Class Notes, Chapter 20</a>
26 <p><B>Exercises:
27 </B></p><OL><li>This week we're going to add containers
28 (so that objects can be put inside of other objects)
29 and attributes,
30 so that we can remember things about objects
31 (such as whether they're containers, whether they're open, etc.).
32 <br>
33 <br>
34 The code to implement this changes is scattered,
35 and not all of it is
36 in the distribution directories.
37 You'll have to type some of this new code in yourself.
38 <br>
39 <br>
40 Here is the modified object structure,
41 along with a few definitions:
42 <pre>
43 struct object
45 char name[MAXNAME];
46 unsigned int attrs;
47 struct object *contents; /* contents (if container) */
48 struct object *lnext; /* next in list of contained objects */
49 /* (i.e. in this object's container) */
50 char *desc; /* long description */
53 #define CONTAINER 0x0001
54 #define CLOSABLE 0x0002
55 #define OPEN 0x0004
56 #define HEAVY 0x0008
57 #define BROKEN 0x0010
58 #define TOOL 0x0020
59 #define SOFT 0x0040
60 #define SHARP 0x0080
61 #define LOCK 0x0100
62 #define KEY 0x0200
63 #define LOCKED 0x0400
64 #define TRANSPARENT 0x0800
65 #define IMMOBILE 0x1000
67 #define Iscontainer(o) ((o)-&gt;attrs &amp; CONTAINER)
68 #define Isopen(o) ((o)-&gt;attrs &amp; OPEN)
69 </pre>
70 (This code is in the file <TT>game.xh</TT> in the <TT>week4</TT> subdirectory,
71 although the copy there is missing the new <TT>desc</TT> field for objects.)
72 <br>
73 <br>
74 Here is new code,
75 for <TT>commands.c</TT>,
76 to implement new ``open,'' ``close,''
77 and ``put (in)'' commands:
78 <pre>
79 else if(strcmp(verb, "open") == 0)
81 if(objp == NULL)
83 printf("You must tell me what to open.\n");
84 return FALSE;
86 if(Isopen(objp))
88 printf("The %s is already open.\n", objp-&gt;name);
89 return FALSE;
91 if(!(objp-&gt;attrs &amp; CLOSABLE))
93 printf("You can't open the %s.\n", objp-&gt;name);
94 return FALSE;
96 objp-&gt;attrs |= OPEN;
97 printf("The %s is now open.\n", objp-&gt;name);
100 else if(strcmp(verb, "close") == 0)
102 if(objp == NULL)
104 printf("You must tell me what to close.\n");
105 return FALSE;
107 if(!(objp-&gt;attrs &amp; CLOSABLE))
109 printf("You can't close the %s.\n", objp-&gt;name);
110 return FALSE;
112 if(!Isopen(objp))
114 printf("The %s is already closed.\n", objp-&gt;name);
115 return FALSE;
117 objp-&gt;attrs &amp;= ~OPEN;
118 printf("The %s is now closed.\n", objp-&gt;name);
121 else if(strcmp(verb, "put") == 0)
123 if(objp == NULL)
125 printf("You must tell me what to put.\n");
126 return FALSE;
128 if(!contains(player-&gt;contents, objp))
130 printf("You don't have the %s.\n", objp-&gt;name);
131 return FALSE;
133 if(cmd-&gt;preposition == NULL || strcmp(cmd-&gt;preposition, "in") != 0 ||
134 cmd-&gt;xobject == NULL)
136 printf("You must tell me where to put the %s.\n",
137 objp-&gt;name);
138 return FALSE;
140 if(!Iscontainer(cmd-&gt;xobject))
142 printf("You can't put things in the %s.\n",
143 cmd-&gt;xobject-&gt;name);
144 return FALSE;
146 if((cmd-&gt;xobject-&gt;attrs &amp; CLOSABLE) &amp;&amp; !Isopen(cmd-&gt;xobject))
148 printf("The %s is closed.\n", cmd-&gt;xobject-&gt;name);
149 return FALSE;
151 if(!putobject(player, objp, cmd-&gt;xobject))
153 /* shouldn't happen */
154 printf("You can't put the %s in the %s!\n",
155 objp-&gt;name, cmd-&gt;xobject-&gt;name);
156 return FALSE;
158 printf("Now the %s is in the %s.\n",
159 objp-&gt;name, cmd-&gt;xobject-&gt;name);
161 </pre>
162 (This code is in the file <TT>week4/commands.xc</TT>.)
163 <br>
164 <br>
165 Here is a new version of <TT>newobject</TT>, for <TT>object.c</TT>,
166 that initializes the new object attribute and contents fields.
167 (It's also missing any required initialization
168 of the new long description field;
169 make sure you preserve yours.)
170 <pre>
171 struct object *
172 newobject(char *name)
174 struct object *objp;
176 if(nobjects &gt;= MAXOBJECTS)
178 fprintf(stderr, "too many objects\n");
179 exit(1);
182 objp = &amp;objects[nobjects++];
184 strcpy(objp-&gt;name, name);
185 objp-&gt;lnext = NULL;
186 objp-&gt;attrs = 0;
187 objp-&gt;contents = NULL;
189 return objp;
191 </pre>
192 Here is the new <TT>putobject</TT> function, for <TT>object.c</TT>.
193 (It's in <TT>week4/object.xc</TT>.)
194 <pre>
195 /* transfer object from actor to container */
197 putobject(struct actor *actp, struct object *objp, struct object *container)
199 struct object *lp;
200 struct object *prevlp = NULL;
202 for(lp = actp-&gt;contents; lp != NULL; lp = lp-&gt;lnext)
204 if(lp == objp) /* found it */
206 /* splice out of actor's list */
208 if(lp == actp-&gt;contents) /* head of list */
209 actp-&gt;contents = lp-&gt;lnext;
210 else prevlp-&gt;lnext = lp-&gt;lnext;
212 /* splice into container's list */
214 lp-&gt;lnext = container-&gt;contents;
215 container-&gt;contents = lp;
217 return TRUE;
220 prevlp = lp;
223 /* didn't find it (error) */
225 return FALSE;
227 </pre>
228 Here is new code for the <TT>parsedatafile</TT> function
229 in <TT>io.c</TT>,
230 to read attributes for objects:
231 <pre>
232 else if(strcmp(av[0], "attribute") == 0)
234 if(currentobject == NULL)
235 continue;
236 if(strcmp(av[1], "container") == 0)
237 currentobject-&gt;attrs |= CONTAINER;
238 else if(strcmp(av[1], "closable") == 0)
239 currentobject-&gt;attrs |= CLOSABLE;
240 else if(strcmp(av[1], "open") == 0)
241 currentobject-&gt;attrs |= OPEN;
242 else if(strcmp(av[1], "heavy") == 0)
243 currentobject-&gt;attrs |= HEAVY;
244 else if(strcmp(av[1], "soft") == 0)
245 currentobject-&gt;attrs |= SOFT;
246 else if(strcmp(av[1], "sharp") == 0)
247 currentobject-&gt;attrs |= SHARP;
249 </pre>
250 (This code is incomplete;
251 at some point you'll have to add cases for the other attributes
252 defined
253 in <TT>game.h</TT>.)
254 <br>
255 <br>
256 Here are the ``break'' and ``cut'' commands
257 from <TT>commands.c</TT>,
258 modified to make use of a few attributes:
259 <pre>
260 else if(strcmp(verb, "break") == 0)
262 if(objp == NULL)
264 printf("You must tell me what to break.\n");
265 return FALSE;
267 if(cmd-&gt;preposition == NULL || strcmp(cmd-&gt;preposition, "with") != 0 ||
268 cmd-&gt;xobject == NULL)
270 printf("You must tell me what to break with.\n");
271 return FALSE;
273 if(!contains(player-&gt;contents, cmd-&gt;xobject))
275 printf("You have no %s.\n", cmd-&gt;xobject-&gt;name);
276 return FALSE;
278 if(!(cmd-&gt;xobject-&gt;attrs &amp; HEAVY))
280 printf("I don't think the %s is heavy enough to break things with.\n",
281 cmd-&gt;xobject-&gt;name);
282 return FALSE;
284 objp-&gt;attrs |= BROKEN;
285 printf("Oh, dear. Now the %s is broken.\n", objp-&gt;name);
288 else if(strcmp(verb, "cut") == 0)
290 if(objp == NULL)
292 printf("You must tell me what to cut.\n");
293 return FALSE;
295 if(cmd-&gt;preposition == NULL || strcmp(cmd-&gt;preposition, "with") != 0 ||
296 cmd-&gt;xobject == NULL)
298 printf("You must tell me what to cut with.\n");
299 return FALSE;
301 if(!contains(player-&gt;contents, cmd-&gt;xobject))
303 printf("You have no %s.\n", cmd-&gt;xobject-&gt;name);
304 return FALSE;
306 if(!(cmd-&gt;xobject-&gt;attrs &amp; SHARP))
308 printf("I don't think the %s is sharp enough to cut things with.\n",
309 cmd-&gt;xobject-&gt;name);
310 return FALSE;
312 if(!(objp-&gt;attrs &amp; SOFT))
314 printf("I don't think you can cut the %s with the %s.\n",
315 objp-&gt;name, cmd-&gt;xobject-&gt;name);
316 return FALSE;
318 printf("The %s is now cut in two.\n", objp-&gt;name);
320 </pre>
321 Try to get all of this code integrated, compiled, and working.
322 You will probably have to add a few prototypes to <TT>game.h</TT>.
323 <br>
324 <br>
325 Once it compiles,
326 before you can play with the new features,
327 you will have to make some changes to the data file,
328 <TT>dungeon.dat</TT>, too.
329 Make the hammer heavy by adding the line
330 <pre>
331 attribute heavy
332 </pre>
333 just after the line
334 <pre>
335 object hammer
336 </pre>
337 Make the kettle a container by adding the line
338 <pre>
339 attribute container
340 </pre>
341 just after the line
342 <pre>
343 object kettle
344 </pre>
345 Add a knife object by adding the lines
346 <pre>
347 object knife
348 attribute sharp
349 </pre>
350 in one of the rooms.
351 Add a box object by adding the lines
352 <pre>
353 object box
354 attribute closable
355 </pre>
356 Now you should be able to break things with the hammer,
357 and put things into the kettle or box
358 (once you open the box).
359 The knife won't cut things unless they're soft,
360 so add an object with
361 <pre>
362 attribute soft
363 </pre>
364 and try cutting it with the knife.
365 Once you put things into containers,
366 they'll seem to disappear;
367 we'll fix that in the next two exercises.
368 <br>
369 <br>
370 With these changes, the game suddenly explodes in potential complexity.
371 There are lots and lots of features you'll be able to add,
372 limited only by your own creativity and the amount of time you
373 care to spend/waste.
374 I've suggested several changes and improvements below,
375 although you don't have to make all of them,
376 and you're not limited to these suggestions, either.
377 (You won't be able to add too many more attributes, though,
378 without possibly running out of bits in an int.
379 We'll start using a more open-ended scheme for implementing
380 attributes next time.
381 In the mean time, you could make
382 the <TT>attrs</TT> field
383 an <TT>unsigned long int</TT>
384 if you were desperate to add more attributes.)
385 <li>Modify the <TT>findobject</TT> function in <TT>object.c</TT> so
386 that it can find objects when they're inside containers
387 (both in the actor's possession and sitting in the room).
388 The original implementation of <TT>findobject</TT>
389 has separate loops for searching the actor's list of objects
390 and the room's list of objects.
391 But now, when an object
392 (in either list)
393 is a container,
394 we'll need to search through <em>its</em> list of
395 (contained)
396 objects, too.
397 It will be easiest if you add a new function,
398 <TT>findobj2</TT>,
400 which searches for an object in any list of objects.
401 The code in <TT>findobj2</TT> will be just like
402 the old code in <TT>findobject</TT>
403 for searching the actor's and room's lists;
404 in fact, once you write <TT>findobj2</TT>,
405 you'll be able to replace the actor- and room-searching code
406 with simple calls to
407 <pre>
408 findobj2(actp-&gt;contents, name);
409 </pre>
411 <pre>
412 findobj2(actp-&gt;location-&gt;contents, name);
413 </pre>
414 Ideally,
415 your scheme should work even to find objects within objects
416 within objects
417 (or deeper);
418 this will involve recursive calls to <TT>findobj2</TT>.
419 <li>Modify the <TT>takeobject</TT> function in <TT>object.c</TT>
420 so that it can also take objects which are sitting in containers
421 (both in the actor's possession and sitting in the room).
422 If a container is <TT>CLOSABLE</TT>, make sure it's <TT>OPEN</TT>!
423 <li>Rewrite the ``examine'' command
424 (in <TT>commands.c</TT>)
425 to mention some of the relevant attributes
426 (<TT>OPEN</TT>, <TT>BROKEN</TT>, etc.)
427 of the object being examined.
428 (Hint: this is a good opportunity to use the conditional or
429 <TT>?:</TT> operator.)
430 <li>Modify the <TT>listobjects</TT> function in <TT>object.c</TT> to
431 list the contents (if any) of objects which are containers.
432 Should it print the contents of closed containers?
433 (In a more complicated game, we might worry about transparency...)
434 <li>Implement objects which can be locked
435 (if they have the <TT>LOCK</TT> attribute).
436 Add a ``lock'' command to lock them
437 (i.e. to set the <TT>LOCKED</TT> attribute)
438 and an ``unlock'' command to unlock them.
439 Both ``lock'' and ``unlock'' should require
440 that the user use a tool which has the <TT>KEY</TT> attribute.
441 (In other words, ``lock strongbox with key'' should
442 work,
443 but ``lock strongbox with broom'' should not work.
444 But don't worry about trying to make certain keys fit only certain locks.)
445 Don't let objects be opened if they have the <TT>LOCK</TT> attribute
446 and are locked.
447 (You'll have to update the attribute-reading code in <TT>io.c</TT>
448 to handle the <TT>LOCK</TT>, <TT>KEY</TT>, and <TT>LOCKED</TT> attributes.)
449 <li>Implement a ``fix'' command which will let the user
450 fix broken objects,
451 as long as the preposition ``with'' appears and the
452 implement is an object with the <TT>TOOL</TT> attribute.
453 (You'll have to update the attribute-reading code in <TT>io.c</TT>
454 to handle the <TT>TOOL</TT> attribute.)
455 <li>Implement immobile objects that can't be picked up.
456 (You'll have to update the attribute-reading code in <TT>io.c</TT>
457 to handle the <TT>IMMOBILE</TT> attribute.)
458 </OL><hr>
459 <hr>
461 This page by <a href="http://www.eskimo.com/~scs/">Steve Summit</a>
462 // <a href="copyright.html">Copyright</a> 1995-9
463 // <a href="mailto:scs@eskimo.com">mail feedback</a>
464 </p>
465 </body>
466 </html>