* remove "\r" nonsense
[mascara-docs.git] / C / the.ansi.c.programming.language / notes.accompany.ansi.c / homework / PS6.html
blobdd9b77cb12fc9fd1fe40ab09f0a980e792590772
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 #6</title>
7 </head>
8 <body>
9 <H1>Assignment #6</H1>
15 <B>Intermediate C Programming
16 <br>
17 <br>
18 UW Experimental College
19 </B><br>
20 <br>
21 <B>Assignment #6
22 </B><p><B>Handouts:
23 </B></p><p><a href="PS6.html">Assignment #6</a>
24 <br><a href="PS5a.html">Assignment #5 Answers</a>
25 <br><a href="http://www.eskimo.com/~scs/cclass/int/sx9.html">Class Notes, Chapter 23</a>
26 <br><a href="http://www.eskimo.com/~scs/cclass/int/sx10.html">Class Notes, Chapter 24</a>
27 <p><B>Exercises:
28 </B></p><OL><li>In this exercise, we'll almost completely rewrite <TT>commands.c</TT>
29 so that instead of having a giant <TT>if</TT>/<TT>else</TT> chain,
30 with many calls to <TT>strcmp</TT>
31 and the code for all the actions interspersed,
32 there will be one function per command,
33 a table relating command verbs to the corresponding functions,
34 and some much simpler code
35 which will look up a command verb in the table
36 and decide which function to call.
37 The functions in the table will,
38 of course,
39 be referred to by function pointers.
40 <br>
41 <br>
42 In the <TT>week6</TT> subdirectory of the distribution materials
43 are two new files, <TT>cmdtab.c</TT> and <TT>cmdtab.h</TT>,
44 and a modified
45 (though incomplete)
46 copy of <TT>commands.c</TT>.
47 <TT>cmdtab.h</TT> defines the command table structure,
48 <TT>struct cmdtab</TT>:
49 <pre>
50 struct cmdtab
52 char *name;
53 int (*func)(struct actor *, struct object *, struct sentence *);
56 extern struct cmdtab *findcmd(char *, struct cmdtab [], int);
57 </pre>
58 <br>
59 <br>
60 With this structure in hand, the first thing to do
61 is to declare a bunch of functions,
62 one for each command,
63 and then build the ``command table,''
64 which is an array of <TT>struct cmdtab</TT>.
65 All of the command functions are going to take as arguments
66 the actor (<TT>struct actor *</TT>),
67 the object being acted upon (<TT>struct object *</TT>, if any),
68 and the complete sentence structure (<TT>struct sentence *</TT>)
69 describing the command we're trying to interpret/implement.
70 (The object being acted upon is the direct object of the sentence
71 and could also be pulled out of the sentence structure;
72 we're passing it as a separate argument
73 because that might make things more convenient
74 for the command functions themselves.)
75 Here, then, is the command table,
76 incorporating all the commands we've ever used
77 (your copy of the game might not include all of the commands referenced here):
78 <pre>
79 static int dircmd(struct actor *, struct object *, struct sentence *);
80 static int takecmd(struct actor *, struct object *, struct sentence *);
81 static int dropcmd(struct actor *, struct object *, struct sentence *);
82 static int lookcmd(struct actor *, struct object *, struct sentence *);
83 static int invcmd(struct actor *, struct object *, struct sentence *);
84 static int examcmd(struct actor *, struct object *, struct sentence *);
85 static int hitcmd(struct actor *, struct object *, struct sentence *);
86 static int breakcmd(struct actor *, struct object *, struct sentence *);
87 static int fixcmd(struct actor *, struct object *, struct sentence *);
88 static int cutcmd(struct actor *, struct object *, struct sentence *);
89 static int readcmd(struct actor *, struct object *, struct sentence *);
90 static int opencmd(struct actor *, struct object *, struct sentence *);
91 static int closecmd(struct actor *, struct object *, struct sentence *);
92 static int lockcmd(struct actor *, struct object *, struct sentence *);
93 static int unlockcmd(struct actor *, struct object *, struct sentence *);
94 static int putcmd(struct actor *, struct object *, struct sentence *);
95 static int helpcmd(struct actor *, struct object *, struct sentence *);
96 static int quitcmd(struct actor *, struct object *, struct sentence *);
98 static struct cmdtab commands[] =
100 "n", dircmd,
101 "north", dircmd,
102 "s", dircmd,
103 "south", dircmd,
104 "e", dircmd,
105 "east", dircmd,
106 "w", dircmd,
107 "west", dircmd,
108 "ne", dircmd,
109 "northeast", dircmd,
110 "se", dircmd,
111 "southeast", dircmd,
112 "nw", dircmd,
113 "northwest", dircmd,
114 "sw", dircmd,
115 "southwest", dircmd,
116 "up", dircmd,
117 "down", dircmd,
118 "take", takecmd,
119 "drop", dropcmd,
120 "look", lookcmd,
121 "i", invcmd,
122 "inventory", invcmd,
123 "examine", examcmd,
124 "hit", hitcmd,
125 "break", breakcmd,
126 "fix", fixcmd,
127 "cut", cutcmd,
128 "read", readcmd,
129 "open", opencmd,
130 "close", closecmd,
131 "lock", lockcmd,
132 "unlock", unlockcmd,
133 "put", putcmd,
134 "?", helpcmd,
135 "help", helpcmd,
136 "quit", quitcmd,
138 </pre>
139 <br>
140 <br>
141 With this structure defined, the body of <TT>docommand</TT>
142 is drastically simplified.
143 In fact, since we'll break the searching of the command table
144 out to a separate function,
145 all the new <TT>docommand</TT> has to do is call that function,
146 then (if the command is found)
147 call the command function associated with the command:
148 <pre>
149 docommand(struct actor *player, struct sentence *cmd)
151 struct cmdtab *cmdtp;
153 cmdtp = findcmd(cmd-&gt;verb, commands, Sizeofarray(commands));
155 if(cmdtp != NULL)
157 (*cmdtp-&gt;func)(player, cmd-&gt;object, cmd);
158 return TRUE;
160 else {
161 printf("I don't know the word \"%s\".\n", cmd-&gt;verb);
162 return FALSE;
165 </pre>
166 The new <TT>findcmd</TT> function wants to know
167 (as its third argument)
168 the number of entries in the command table.
169 It would be a nuisance if we had to count them,
170 and we'd have to keep updating our count each time
171 we added an entry to the table.
172 The built-in <TT>sizeof</TT> operator can tell us the size of the array,
173 but it tells us the size in bytes,
174 while we want to know the number of entries.
175 But the number of entries is
176 the size of the array in bytes divided by the size of one entry in bytes,
178 <pre>
179 sizeof(commands) / sizeof(commands[0])
180 </pre>
181 (here we get a handle on ``the size of one entry''
182 by arbitrarily taking <TT>sizeof(commands[0])</TT>).
183 Since this is a common operation,
184 I like to define a function-like macro to encapsulate it:
185 <pre>
186 #define Sizeofarray(a) (sizeof(a) / sizeof(a[0]))
187 </pre>
188 With that definition in place,
189 the number of entries in the <TT>commands</TT> array is simply
190 <TT>Sizeofarray(commands)</TT>.
191 <br>
192 <br>
193 Here is the <TT>findcmd</TT> function, from <TT>cmdtab.c</TT>:
194 <pre>
195 #include &lt;string.h&gt;
196 #include "cmdtab.h"
198 struct cmdtab *
199 findcmd(char *cmd, struct cmdtab cmdtab[], int ncmds)
201 int i;
203 for(i = 0; i &lt; ncmds; i++)
205 if(strcmp(cmdtab[i].name, cmd) == 0)
206 return &amp;cmdtab[i];
209 return NULL;
211 </pre>
212 <br>
213 <br>
214 The ``only'' thing left to do
215 is to write all of the individual command functions.
216 The code for all of them
217 will come from the corresponding <TT>if</TT> clause
218 from the old, bulky version of <TT>docommand</TT>.
219 For example, here is <TT>takecmd</TT>:
220 <pre>
222 static int
223 takecmd(struct actor *player, struct object *objp,
224 struct sentence *cmd)
226 if(objp == NULL)
228 printf("You must tell me what to take.\n");
229 return FALSE;
231 if(contains(player-&gt;contents, objp))
233 printf("You already have the %s.\n", objp-&gt;name);
234 return FALSE;
236 if(hasattr(objp, "immobile"))
238 printf("The %s cannot be picked up.\n", objp-&gt;name);
239 return FALSE;
241 if(!takeobject(player, objp))
243 /* shouldn't happen */
244 printf("You can't pick up the %s.\n", objp-&gt;name);
245 return FALSE;
247 printf("Taken.\n");
249 return SUCCESS;
251 </pre>
252 With one exception,
253 all of the rest of the command functions are similar;
254 they just have
255 <pre>
256 static int
257 <I>xxx</I>cmd(struct actor *actor, struct object *objp,
258 struct sentence *cmd)
261 return SUCCESS;
263 </pre>
264 wrapped around the code that used to be in one of the
265 <pre>
266 else if(strcmp(verb, "<I>xxx</I>") == 0)
270 </pre>
271 blocks.
272 <br>
273 <br>
274 (Although the copy of <TT>commands.c</TT> in the <TT>week6</TT> subdirectory
275 reflects this structure,
276 you probably won't be able to use that file directly,
277 because it does not reflect
278 all of the many
279 changes and additions
280 we've been making to <TT>commands.c</TT> over the past several weeks.)
281 <br>
282 <br>
283 The integer code <TT>SUCCESS</TT> that each of these functions is returning
284 isn't really used yet, so you don't have to worry about what it's for.
285 (Right now, <TT>docommand</TT> is ignoring the return value
286 of the called command function.)
287 <br>
288 <br>
289 The one command function that doesn't quite fit the pattern is <TT>dircmd</TT>,
290 because it's called for any of the direction commands,
291 and has to take another look at the command verb
292 to see what direction it represents:
293 <pre>
294 static int
295 dircmd(struct actor *player, struct object *objp,
296 struct sentence *cmd)
298 struct room *roomp = player-&gt;location;
299 char *verb = cmd-&gt;verb;
300 int dir;
302 if(strcmp(verb, "n") == 0 || strcmp(verb, "north") == 0)
303 dir = NORTH;
304 else if(strcmp(verb, "s") == 0 || strcmp(verb, "south") == 0)
305 dir = SOUTH;
306 else if(strcmp(verb, "e") == 0 || strcmp(verb, "east") == 0)
307 dir = EAST;
308 else if(strcmp(verb, "w") == 0 || strcmp(verb, "west") == 0)
309 dir = WEST;
310 else if(strcmp(verb, "ne") == 0 || strcmp(verb, "northeast") == 0)
311 dir = NORTHEAST;
312 else if(strcmp(verb, "se") == 0 || strcmp(verb, "southeast") == 0)
313 dir = SOUTHEAST;
314 else if(strcmp(verb, "nw") == 0 || strcmp(verb, "northwest") == 0)
315 dir = NORTHWEST;
316 else if(strcmp(verb, "sw") == 0 || strcmp(verb, "southwest") == 0)
317 dir = SOUTHWEST;
318 else if(strcmp(verb, "up") == 0)
319 dir = UP;
320 else if(strcmp(verb, "down") == 0)
321 dir = DOWN;
322 else return ERROR;
324 if(roomp == NULL)
326 printf("Where are you?\n");
327 return ERROR;
330 /* If the exit does not exist, or if gotoroom() fails, */
331 /* the player can't go that way. */
333 if(roomp-&gt;exits[dir] == NULL || !gotoroom(player, roomp-&gt;exits[dir]))
335 printf("You can't go that way.\n");
336 return ERROR;
339 /* player now in new room */
341 listroom(player);
343 return SUCCESS;
345 </pre>
346 This function also returns another status code, <TT>ERROR</TT>
347 (which is also ignored, for now, by <TT>docommand</TT>).
348 If your copy of <TT>game.h</TT> doesn't include them,
349 you can put the lines
350 <pre>
351 #define ERROR 0
352 #define SUCCESS 1
353 </pre>
354 in it to <TT>#define</TT> these values.
355 <li>Modify <TT>io.c</TT> to recognize a new <TT>entrypoint</TT> line
356 in the data file,
357 which will list the name of the room
358 which the actor should be placed in to start.
359 What if the <TT>entrypoint</TT> line
360 precedes the description of the room it names?
361 How should the code in <TT>io.c</TT> record the entry-point room
362 so that <TT>getentryroom</TT> in <TT>rooms.c</TT> can use it?
363 (There are many answers to these questions;
364 pick one, and implement it.)
365 <li>Another part of the game
366 that has the same sort of large,
367 <TT>if</TT>/<TT>strcmp</TT>/<TT>else</TT> structure
368 as did <TT>docommand</TT> in <TT>commands.c</TT>
370 <TT>parsedatafile</TT> in <TT>io.c</TT>.
371 Think about what it would take
372 to use the <TT>cmdtab</TT> structure,
373 and the <TT>findcmd</TT> function,
374 to streamline the code that processes lines in the data file.
375 <br>
376 <br>
377 (If you try it, you'll probably run into some difficulties
378 which will make it rather hard to finish,
379 so save a copy of <TT>io.c</TT> before you start!
380 In the answers handed out next week,
381 I'll describe the difficulties,
382 and outline how they might be circumvented.)
383 </OL><hr>
384 <hr>
386 This page by <a href="http://www.eskimo.com/~scs/">Steve Summit</a>
387 // <a href="copyright.html">Copyright</a> 1995-9
388 // <a href="mailto:scs@eskimo.com">mail feedback</a>
389 </p>
390 </body>
391 </html>