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 #
6</title>
15 <B>Intermediate C Programming
18 UW Experimental College
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>
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,
39 be referred to by function pointers.
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>,
46 copy of
<TT>commands.c
</TT>.
47 <TT>cmdtab.h
</TT> defines the command table structure,
48 <TT>struct cmdtab
</TT>:
53 int (*func)(struct actor *, struct object *, struct sentence *);
56 extern struct cmdtab *findcmd(char *, struct cmdtab [], int);
60 With this structure in hand, the first thing to do
61 is to declare a bunch of functions,
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):
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[] =
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:
149 docommand(struct actor *player, struct sentence *cmd)
151 struct cmdtab *cmdtp;
153 cmdtp = findcmd(cmd-
>verb, commands, Sizeofarray(commands));
157 (*cmdtp-
>func)(player, cmd-
>object, cmd);
161 printf(
"I don't know the word \"%s\
".\n", cmd-
>verb);
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,
179 sizeof(commands) / sizeof(commands[
0])
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:
186 #define Sizeofarray(a) (sizeof(a) / sizeof(a[
0]))
188 With that definition in place,
189 the number of entries in the
<TT>commands
</TT> array is simply
190 <TT>Sizeofarray(commands)
</TT>.
193 Here is the
<TT>findcmd
</TT> function, from
<TT>cmdtab.c
</TT>:
195 #include
<string.h
>
199 findcmd(char *cmd, struct cmdtab cmdtab[], int ncmds)
203 for(i =
0; i
< ncmds; i++)
205 if(strcmp(cmdtab[i].name, cmd) ==
0)
206 return
&cmdtab[i];
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>:
223 takecmd(struct actor *player, struct object *objp,
224 struct sentence *cmd)
228 printf(
"You must tell me what to take.\n");
231 if(contains(player-
>contents, objp))
233 printf(
"You already have the %s.\n", objp-
>name);
236 if(hasattr(objp,
"immobile"))
238 printf(
"The %s cannot be picked up.\n", objp-
>name);
241 if(!takeobject(player, objp))
243 /* shouldn't happen */
244 printf(
"You can't pick up the %s.\n", objp-
>name);
253 all of the rest of the command functions are similar;
257 <I>xxx
</I>cmd(struct actor *actor, struct object *objp,
258 struct sentence *cmd)
264 wrapped around the code that used to be in one of the
266 else if(strcmp(verb,
"<I>xxx</I>") ==
0)
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
279 changes and additions
280 we've been making to
<TT>commands.c
</TT> over the past several weeks.)
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.)
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:
295 dircmd(struct actor *player, struct object *objp,
296 struct sentence *cmd)
298 struct room *roomp = player-
>location;
299 char *verb = cmd-
>verb;
302 if(strcmp(verb,
"n") ==
0 || strcmp(verb,
"north") ==
0)
304 else if(strcmp(verb,
"s") ==
0 || strcmp(verb,
"south") ==
0)
306 else if(strcmp(verb,
"e") ==
0 || strcmp(verb,
"east") ==
0)
308 else if(strcmp(verb,
"w") ==
0 || strcmp(verb,
"west") ==
0)
310 else if(strcmp(verb,
"ne") ==
0 || strcmp(verb,
"northeast") ==
0)
312 else if(strcmp(verb,
"se") ==
0 || strcmp(verb,
"southeast") ==
0)
314 else if(strcmp(verb,
"nw") ==
0 || strcmp(verb,
"northwest") ==
0)
316 else if(strcmp(verb,
"sw") ==
0 || strcmp(verb,
"southwest") ==
0)
318 else if(strcmp(verb,
"up") ==
0)
320 else if(strcmp(verb,
"down") ==
0)
326 printf(
"Where are you?\n");
330 /* If the exit does not exist, or if gotoroom() fails, */
331 /* the player can't go that way. */
333 if(roomp-
>exits[dir] == NULL || !gotoroom(player, roomp-
>exits[dir]))
335 printf(
"You can't go that way.\n");
339 /* player now in new room */
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
354 in it to
<TT>#define
</TT> these values.
355 <li>Modify
<TT>io.c
</TT> to recognize a new
<TT>entrypoint
</TT> line
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.
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.)
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>