* better
[mascara-docs.git] / lang / C / the.ansi.c.programming.language / notes.accompany.ansi.c / homework / PS2a.html
blob3ccf4b9514df0987448655abc9cdeb622b671eb2
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 #2 Answers</title>
7 </head>
8 <body>
9 <H1>Assignment #2 Answers</H1>
15 <B>Intermediate C Programming
16 <br>
17 <br>
18 UW Experimental College
19 </B><br>
20 <br>
21 <B>Assignment #2 ANSWERS
22 </B><br>
23 <br>
24 <p>Question 1.
25 <I>List some differences between text and binary data files.
26 </I><p>Text files store lines of characters,
27 intended to be human-readable
28 if printed,
29 displayed to the screen,
30 read into a text editor, etc.
31 Binary files contain arbitrary byte values
32 which make little or no sense if interpreted as characters.
33 <p>Binary files may be smaller,
34 and if read and written in a simpleminded way,
35 they may be more efficient to read and write,
36 and the code to do so may be easier to write.
37 Text files tend to be larger,
38 to take longer
39 (than binary files)
40 to read and write,
41 and to require more elaborate code.
42 (However, if binary files are read in safer ways,
43 or written in more portable ways,
44 the code to do so is as complicated as for text files,
45 and will probably be intermediate in efficiency
46 between text files and simpleminded binary files.)
47 <p>Text files are quite portable.
48 Binary files will not necessarily work if they are moved
49 between machines having different word sizes or data formats,
50 or between compilers which lay out data structures differently in memory.
51 <p>It's easy to create, inspect, and modify text files using standard tools.
52 Binary file maintenance generally requires specially-written tools.
53 <p>Binary files must be written using <TT>fopen</TT> mode <TT>"wb"</TT>
54 and read using mode <TT>"rb"</TT>
55 <p>Exercise 2.
56 <I>Devise and implement a way for
57 long descriptions to be read from the data file.
58 </I><p>There are many ways of doing this.
59 All are a bit tricky,
60 given certain constraints imposed by the existing code.
61 <p>The most obvious way is to use lines of the form
62 <pre>
63 desc You are in a dark and gloomy cave.
64 </pre>
65 in the data file.
66 (Such lines could of course be used for objects as well.)
67 The only problem with this approach
68 is that in the existing data file reading code in <TT>io.c</TT>,
69 each line read from the data file
70 is immediately broken up into words by calling <TT>getwords</TT>,
71 and this break-up happens
72 before it's decided
73 what keyword began the line
74 and what's to be done with the rest of the information on the line.
75 In the case of long descriptions,
76 we want the rest of the text on the line
77 (after the keyword <TT>desc</TT>)
78 to be treated as a single sting,
79 not a series of individual words.
80 <p>One way around this problem
81 is to make a copy of the line read from the data file
82 before breaking it up with <TT>getwords</TT>.
83 I declared a second array
84 <pre>
85 char line2[MAXLINE];
86 </pre>
87 at the top of <TT>parsedatafile</TT> in <TT>io.c</TT>,
88 and changed the old line
89 <pre>
90 ac = getwords(line, av, MAXARGS);
91 </pre>
93 <pre>
94 strcpy(line2, line);
95 ac = getwords(line2, av, MAXARGS);
96 </pre>
97 Now <TT>line2</TT> is broken up by <TT>getwords</TT>
98 (and <TT>av</TT> ends up containing pointers into <TT>line2</TT>),
99 but the contents of <TT>line</TT> remain intact.
100 <p>Then I added this case
101 to the <TT>if</TT>/<TT>else</TT> chain
102 that makes up the bulk of <TT>parsedatafile</TT>:
103 <pre>
104 else if(strcmp(av[0], "desc") == 0)
106 char *p;
107 for(p = line; *p != '\0' &amp;&amp; !isspace(*p); p++) /* skip "desc" */
109 for(; isspace(*p); p++) /* skip whitespace */
111 if(currentobject != NULL)
112 currentobject-&gt;desc = chkstrdup(p);
113 else if(currentroom != NULL)
114 currentroom-&gt;desc = chkstrdup(p);
115 else fprintf(stderr, "desc not in object or room\n");
117 </pre>
118 The code has two more troublesome spots:
119 first,
120 since the <TT>line</TT> array hasn't been broken into words
121 (which was the point, so as not to break up the descriptive text),
122 we have to manually skip over
123 the keyword ``<TT>desc</TT>'' and the whitespace
124 to find the beginning of the description itself.
125 Second,
126 since I've declared the long descriptions
127 in the object and room structures
128 as <TT>char *</TT>,
129 I have to allocate memory
130 to hold a copy of the description.
131 I do that by calling the function <TT>chkstrdup</TT>,
132 which makes a duplicate copy of a string in newly-allocated memory,
133 checking the return value of the memory allocation for me
134 so that I don't have to.
135 (On another sheet you'll find
136 the code for <TT>chkstrdup</TT>
137 and another function <TT>chkmalloc</TT> which it calls.
138 <TT>chkstrdup</TT> is declared in <TT>chkmalloc.h</TT>;
139 both <TT>chkmalloc.c</TT> and <TT>chkmalloc.h</TT>
140 can be found in the <TT>week5</TT> subdirectory of the source tree.)
141 We'll have much more to say about dynamic memory allocation
142 in upcoming weeks,
143 so don't worry about the details of <TT>chkstrdup</TT> too much.
144 <p>If you chose to use character arrays
145 as the long description fields in the object and room structures,
146 you don't have to worry about memory allocation,
147 but can instead use something like
148 <pre>
149 strcpy(currentobject-&gt;desc, p);
150 </pre>
151 to copy the long description to your array.
152 Just make sure you understand why
153 a single call to <TT>strcpy</TT> would <em>not</em> work
154 if <TT>desc</TT> is declared as <TT>char *</TT>,
155 and why the simple assignment
156 <pre>
157 currentobject-&gt;desc = p;
158 </pre>
159 would not work
160 if <TT>desc</TT> was an array <em>or</em> a pointer
161 (for a different reason in each case).
162 <p>In a real game,
163 room descriptions are likely to be more like a paragraph in length,
164 so cramming them all onto one <TT>desc</TT> line in the data file
165 is quite cumbersome.
166 How could we spread them across multiple lines in the data file?
167 One way is by allowing multiple <TT>desc</TT> lines,
168 and arranging that they all get concatenated together
169 to form the complete description.
170 Here's how I implemented that:
171 <pre>
172 else if(strcmp(av[0], "desc") == 0)
174 char *p;
175 char *newstr;
176 for(p = line; *p != '\0' &amp;&amp; !isspace(*p); p++) /* skip "desc" */
178 for(; isspace(*p); p++) /* skip whitespace */
180 if(currentobject != NULL)
182 if(currentobject-&gt;desc == NULL)
183 currentobject-&gt;desc = chkstrdup(p);
184 else {
185 newstr = chkmalloc(
186 strlen(currentobject-&gt;desc) + 1
187 + strlen(p) + 1);
188 strcpy(newstr, currentobject-&gt;desc);
189 strcat(newstr, "\n");
190 strcat(newstr, p);
191 free(currentobject-&gt;desc);
192 currentobject-&gt;desc = newstr;
195 else if(currentroom != NULL)
197 if(currentroom-&gt;desc == NULL)
198 currentroom-&gt;desc = chkstrdup(p);
199 else {
200 newstr = chkmalloc(
201 strlen(currentroom-&gt;desc) + 1
202 + strlen(p) + 1);
203 strcpy(newstr, currentroom-&gt;desc);
204 strcat(newstr, "\n");
205 strcat(newstr, p);
206 free(currentroom-&gt;desc);
207 currentroom-&gt;desc = newstr;
210 else fprintf(stderr, "desc not in object or room\n");
212 </pre>
213 Now the memory allocation is more complicated,
214 because when we already have a (partial) long description string
215 for a given room or object,
216 we have to allocate a new, longer one and free the old one.
217 (There's an easier way to do this,
218 involving another standard library memory allocation function
219 called <TT>realloc</TT>.
220 We'll see it later.)
221 <p>Another option would be to decide that
222 long descriptions span many lines on the data file,
223 followed by some terminator,
224 without having each descriptive line preceded by any keyword.
225 For example, we might have lines in the data file like this:
226 <pre>
227 desc
228 You are in a dark and gloomy cave.
229 The air is stale and damp.
230 In the shadows you can hear various squeaking and chattering noises.
231 desc end
232 </pre>
233 Here, the word <TT>desc</TT> alone on a line
234 indicates that a long description follows,
235 up to the line containing <TT>desc end</TT>.
236 <p>I implemented that option like this.
237 Notice that the description-reading code
238 therefore has to make more calls to <TT>fgetline</TT>
239 (i.e. besides the main one
240 at the top of the loop in <TT>parsedatafile</TT>).
241 In this case,
242 we don't have to worry about
243 the allocation of <TT>line</TT> vs. <TT>line2</TT>,
244 and we also don't have to skip over anything
245 to find the start of each descriptive line.
246 <pre>
247 else if(strcmp(av[0], "desc") == 0)
249 char *newstr;
250 while(fgetline(fp, line, MAXLINE) != EOF)
252 if(strcmp(line, "desc end") == 0)
253 break;
255 if(currentobject != NULL)
257 if(currentobject-&gt;desc == NULL)
258 currentobject-&gt;desc = chkstrdup(line);
259 else {
260 newstr = chkmalloc(
261 strlen(currentobject-&gt;desc) + 1
262 + strlen(line) + 1);
263 strcpy(newstr, currentobject-&gt;desc);
264 strcat(newstr, "\n");
265 strcat(newstr, line);
266 free(currentobject-&gt;desc);
267 currentobject-&gt;desc = newstr;
270 else if(currentroom != NULL)
272 if(currentroom-&gt;desc == NULL)
273 currentroom-&gt;desc = chkstrdup(line);
274 else {
275 newstr = chkmalloc(
276 strlen(currentroom-&gt;desc) + 1
277 + strlen(line) + 1);
278 strcpy(newstr, currentroom-&gt;desc);
279 strcat(newstr, "\n");
280 strcat(newstr, line);
281 free(currentroom-&gt;desc);
282 currentroom-&gt;desc = newstr;
287 </pre>
288 <p>Exercise 3.
289 <I>Expand the set of allowable exits from rooms.
290 </I><p>I modified the room structure in <TT>game.h</TT> like this:
291 <pre>
292 #define NEXITS 10
294 struct room
296 char name[MAXNAME];
297 struct object *contents;
298 struct room *exits[NEXITS];
299 char *desc; /* long description */
302 /* direction indices in exits array: */
304 #define NORTH 0
305 #define SOUTH 1
306 #define EAST 2
307 #define WEST 3
308 #define NORTHEAST 4
309 #define SOUTHEAST 5
310 #define NORTHWEST 6
311 #define SOUTHWEST 7
312 #define UP 8
313 #define DOWN 9
314 </pre>
315 (By adding the new directions at the end of the array,
316 rather than interspersing them with the original four,
317 any array initializations
318 such as we used to have in <TT>main.c</TT>
319 would still work,
320 since it's legal to supply fewer initializers for an array
321 than the maximum number of elements in the array.)
322 <p>I added this code to <TT>docommand</TT> in <TT>commands.c</TT>:
323 <pre>
324 else if(strcmp(verb, "ne") == 0 || strcmp(verb, "northeast") == 0)
325 dircommand(player, NORTHEAST);
326 else if(strcmp(verb, "se") == 0 || strcmp(verb, "southeast") == 0)
327 dircommand(player, SOUTHEAST);
328 else if(strcmp(verb, "nw") == 0 || strcmp(verb, "northwest") == 0)
329 dircommand(player, NORTHWEST);
330 else if(strcmp(verb, "sw") == 0 || strcmp(verb, "southwest") == 0)
331 dircommand(player, SOUTHWEST);
332 else if(strcmp(verb, "up") == 0)
333 dircommand(player, UP);
334 else if(strcmp(verb, "down") == 0)
335 dircommand(player, DOWN);
336 </pre>
337 <p>I added this code to the <TT>roomexits</TT> case
338 in <TT>parsedatafile</TT> in <TT>io.c</TT>:
339 <pre>
340 else if(strcmp(av[i], "ne") == 0)
341 dir = NORTHEAST;
342 else if(strcmp(av[i], "se") == 0)
343 dir = SOUTHEAST;
344 else if(strcmp(av[i], "nw") == 0)
345 dir = NORTHWEST;
346 else if(strcmp(av[i], "sw") == 0)
347 dir = SOUTHWEST;
348 else if(strcmp(av[i], "u") == 0)
349 dir = UP;
350 else if(strcmp(av[i], "d") == 0)
351 dir = DOWN;
352 </pre>
353 <hr>
354 <hr>
356 This page by <a href="http://www.eskimo.com/~scs/">Steve Summit</a>
357 // <a href="copyright.html">Copyright</a> 1995-9
358 // <a href="mailto:scs@eskimo.com">mail feedback</a>
359 </p>
360 </body>
361 </html>