fix: superhack input path reading
[wumpus.git] / superhack.c
1 /*
2  * superhack.c --- modern version of a classic adventure game
3  *
4  * Author: Eric S. Raymond <esr@snark.thyrsus.com>
5  *
6  * My update of a classic adventure game.  This code is no relation to
7  * the elaborate dungeon game called `Hack'.
8  *
9  * Any resemblance to persons living or dead is strictly coincidental.  And
10  * if you believe *that*...
11  */
12
13 #include <stdio.h>
14 #include <ctype.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
18 #include <time.h>
19 #include <sys/socket.h>
20
21 static int path[5];
22 static int j, k, scratchloc, pies;
23 static char inp[BUFSIZ];                /* common input buffer */
24
25 #define NUMBERS "0123456789"
26 #define YOU     0
27 #define RMS     1
28 #define STARLET1        2
29 #define STARLET2        3
30 #define DROID1  4
31 #define DROID2  5
32 #define LUSER1  6
33 #define LUSER2  7
34 #define LOCS    8
35 static int loc[LOCS];
36
37 #define NOT     0
38 #define WIN     1
39 #define LOSE    -1
40 static int finished;
41
42 #define IGNORE(r) do{if(r);}while(0)
43
44 static int cave[20][3] =
45 {
46     {1,4,7},
47     {0,2,9},
48     {1,3,11},
49     {2,4,13},
50     {0,3,5},
51     {4,6,14},
52     {5,7,16},
53     {0,6,8},
54     {7,9,17},
55     {1,8,10},
56     {9,11,18},
57     {2,10,12},
58     {11,13,19},
59     {3,12,14},
60     {5,13,15},
61     {14,16,19},
62     {6,15,17},
63     {8,16,18},
64     {10,17,19},
65     {12,15,18},
66 };
67
68 #define FNA() (rand() % 20) 
69
70 #define FNB() (rand() % 3) 
71
72 #define FNC() (rand() % 4) 
73
74 int getlet(prompt)
75 char *prompt;
76 {
77     (void) printf("%s? ", prompt);
78     if (fgets(inp, sizeof(inp), stdin))
79       return(tolower(inp[0]));
80     else {
81       fputs("\n",stdout);
82       exit(1);
83     }
84 }
85
86 #define PM(x)   puts(x);
87
88 void print_instructions()
89 {
90     PM("Welcome to `Hunt the Superhack'\n")
91
92 PM("   The superhack lives on the 9th floor of 45 Technology Square in");
93 PM("Cambridge, Massachusetts.  Your mission is to throw a pie in his face.\n");
94
95     PM("   First, you'll have to find him.  A botched experiment by an MIT");
96     PM("physics group has regularized the floor's topology, so that each");
97     PM("room has exits to three other rooms.  (Look at a dodecahedron to");
98     PM("see how this works --- if you don't know what a dodecahedron is,");
99     PM("ask someone.)\n");
100
101     PM("You:");
102     PM("   Each turn you may move to an adjacent room or throw a pie.  If");
103     PM("you run out of pies, you lose.  Each pie can pass through up to");
104     PM("five rooms (connected by a continuous path from where you are).  You");
105     PM("aim by telling the computer which rooms you want to throw through.");
106     PM("If the path is incorrect (presumes a nonexistent connection) the ");
107     PM("pie moves at random.");
108
109     PM("   If a pie hits the superhack, you win. If it hits you, you lose!\n");
110
111     (void) fputs("<Press return to continue>", stdout);
112     IGNORE(fgets(inp, sizeof(inp), stdin));
113     (void) putchar('\n');
114
115     PM("Hazards:");
116     PM("   Starlets --- two rooms contain lonely, beautiful women.  If you");
117     PM("enter these, you will become fascinated and forget your mission as");
118     PM("you engage in futile efforts to pick one up.  You weenie.");
119     PM("   Droids --- two rooms are guarded by experimental AI security ");
120     PM("droids.  If you enter either, the droid will grab you and hustle");
121     PM("you off to somewhere else, at random.");
122     PM("   Lusers --- two rooms contain hungry lusers.  If you blunder into");
123     PM("either, they will eat one of your pies.");
124     PM("   Superhack --- the superhack is not bothered by hazards (the");
125     PM("lusers are in awe of him, he's programmed the droids to ignore him,");
126     PM("and he has no sex life).  Usually he is hacking.  Two things can");
127     PM("interrupt him; you throwing a pie or you entering his room.\n");
128     PM("   On an interrupt, the superhack moves (3/4 chance) or stays where");
129     PM("he is (1/4 chance).  After that, if he is where you are, he flames");
130     PM("you and you lose!\n");
131
132     (void) fputs("<Press return to continue>", stdout);
133     (void) fgets(inp, sizeof(inp), stdin);
134     (void) putchar('\n');
135
136     PM("Warnings:");
137     PM("   When you are one room away from the superhack or a hazard,");
138     PM("the computer says:");
139     PM("   superhack:       \"I smell a superhack!\"");
140     PM("   security droid:  \"Droids nearby!\"");
141     PM("   starlet:         \"I smell perfume!\"");
142     PM("   luser:           \"Lusers nearby!\"");
143
144     PM("If you take too long finding the superhack, hazards may move.  You");
145     PM("will get a warning when this happens.\n");
146
147     PM("Commands:");
148     PM("   Available commands are:\n");
149     PM("  ?            --- print long instructions.");
150     PM("  m <number>   --- move to room with given number.");
151     PM("  t <numbers>  --- throw through given rooms.");
152 #ifdef DEBUG
153     PM("  d            --- dump hazard locations.");
154 #endif /* DEBUG */
155     PM("\nThe list of room numbers after t must be space-separated.  Anything");
156     PM("other than one of these commands displays a short help message.");
157 }
158
159 void move_hazard(where)
160 int where;
161 {
162     int newloc;
163
164  retry:
165     newloc = FNA();
166     for (j = 0; j < LOCS; j++)
167         if (loc[j] == newloc)
168             goto retry;
169
170     loc[where] = newloc;
171 }
172
173 void check_hazards()
174 {
175     /* basic status report */
176     (void) printf("You are in room %d.  Exits lead to %d, %d, %d.  You have %d pies left.\n",
177                   loc[YOU]+1,
178                   cave[loc[YOU]][0]+1,
179                   cave[loc[YOU]][1]+1,
180                   cave[loc[YOU]][2]+1,
181                   pies);
182
183     /* maybe it's migration time */
184     if (FNA() == 0)
185         switch(2 + FNB())
186         {
187         case STARLET1:
188         case STARLET2:
189             PM("Swish, swish, swish --- starlets are moving!");
190             move_hazard(STARLET1);
191             move_hazard(STARLET2);
192             break;
193
194         case DROID1:
195         case DROID2:
196             PM("Clank, clank, clank --- droids are moving!");
197             move_hazard(DROID1);
198             move_hazard(DROID2);
199             break;
200
201         case LUSER1:
202         case LUSER2:
203             PM("Grumble, grumble, grumble --- lusers are moving!");
204             move_hazard(LUSER1);
205             move_hazard(LUSER2);
206             break;
207         }
208
209     /* display hazard warnings */
210     for (k = 0; k < 3; k++)
211     {
212         int room = cave[loc[YOU]][k];
213
214         if (room == loc[RMS])
215             (void) puts("I smell a superhack!");
216         else if (room == loc[STARLET1] || room == loc[STARLET2])
217             (void) puts("I smell perfume!");
218         else if (room == loc[DROID1] || room == loc[DROID2])
219             (void) puts("Droids nearby!");
220         else if (room == loc[LUSER1] || room == loc[LUSER2])
221             (void) puts("Lusers nearby!");
222     }
223 }
224
225 void throw()
226 {
227     extern void check_shot(), move_superhack();
228     int j9;
229
230     j9 = sscanf(inp + strcspn(inp, NUMBERS), "%d %d %d %d %d",
231                     &path[0], &path[1], &path[2], &path[3], &path[4]);
232
233     if (j9 < 1)
234     {
235         PM("Sorry, I didn't see any room numbers after your throw command.");
236         return;
237     }
238
239     for (k = 0; k < j9; k++)
240         if (k >= 2 && path[k] == path[k - 2])
241         {
242             (void) puts("Pies can't fly that crookedly --- try again.");
243             return;
244         }
245
246     scratchloc = loc[YOU];
247
248     for (k = 0; k < j9; k++)
249     {
250         int     k1;
251
252         for (k1 = 0; k1 < 3; k1++)
253         {
254             if (cave[scratchloc][k1] == path[k])
255             {
256                 scratchloc = path[k];
257                 check_shot();
258                 if (finished != NOT)
259                     return;
260             }
261
262         }
263
264         scratchloc = cave[scratchloc][FNB()];
265
266         check_shot();
267
268     }
269
270     if (finished == NOT)
271     {
272         (void) puts("You missed.");
273
274         scratchloc = loc[YOU];
275
276         move_superhack();
277
278         if (--pies <= 0)
279             finished = LOSE;
280     }
281
282 }
283
284 void check_shot()
285 {
286     if (scratchloc == loc[RMS])
287     {
288         (void) puts("Splat!  You got the superhack!  You win.");
289         finished = WIN;
290     }
291
292     else if (scratchloc == loc[YOU])
293     {
294         (void) puts("Ugh!  The pie hit you!  You lose.");
295         finished = LOSE;
296     }
297 }
298
299 void move_superhack()
300 {
301     k = FNC();
302
303     if (k < 3)
304         loc[RMS] = cave[loc[RMS]][k];
305
306     if (loc[RMS] != loc[YOU])
307         return;
308
309     (void) puts("The superhack flames you to a crisp.  You lose!");
310
311     finished = LOSE;
312
313 }
314
315 void move()
316 {
317     if (sscanf(inp + strcspn(inp, NUMBERS), "%d", &scratchloc) < 1)
318     {
319         PM("Sorry, I didn't see a room number after your `m' command.");
320         return;
321     }
322
323     scratchloc--;
324
325     for (k = 0; k < 3; k++)
326         if (cave[loc[YOU]][k] == scratchloc)
327             goto goodmove;
328
329     PM("You can't get there from here!");
330     return;
331
332 goodmove:
333     loc[YOU] = scratchloc;
334
335     if (scratchloc == loc[RMS])
336     {
337         PM("Yow! You interrupted the superhack.");
338         move_superhack();
339     }
340     else if (scratchloc == loc[STARLET1] || scratchloc == loc[STARLET2])
341     {
342         PM("You begin to babble at an unimpressed starlet.  You lose!");
343         finished = LOSE;
344     }
345     else if (scratchloc == loc[DROID1] || scratchloc == loc[DROID2])
346     {
347         PM("Zap --- security droid snatch.  Elsewheresville for you!");
348         scratchloc = loc[YOU] = FNA();
349         goto goodmove;
350     }
351     else if (scratchloc == loc[LUSER1] || scratchloc == loc[LUSER2])
352     {
353         PM("Munch --- lusers ate one of your pies!");
354         pies--;
355     }
356 }
357
358 int main(int argc, char *argv[])
359 {
360     if (argc >= 2 && strcmp(argv[1], "-s") == 0)
361         srand(atoi(argv[2]));
362     else
363         srand((int)time((long *) 0));
364
365     for (;;)
366     {
367     badlocs:
368         for (j = 0; j < LOCS; j++)
369             loc[j] = FNA();
370
371         for (j = 0; j < LOCS; j++)
372             for (k = 0; k < LOCS; k++)
373                 if (j == k)
374                     continue;
375                 else if (loc[j] == loc[k])
376                     goto badlocs;
377
378         (void) puts("Hunt the Superhack");
379
380         pies = 5;
381         scratchloc = loc[YOU];
382         finished = NOT;
383
384         while (finished == NOT)
385         {
386             int c;
387
388             check_hazards();
389
390             c = getlet("Throw, move or help [t,m,?]");
391
392             if (c == 't')
393                 throw();
394             else if (c == 'm')
395                 move();
396             else if (c == '?')
397                 print_instructions();
398 #ifdef DEBUG
399             else if (c == 'd')
400             {
401                 (void) printf("RMS is at %d, starlets at %d/%d, droids %d/%d, lusers %d/%d\n",
402                               loc[RMS] + 1,
403                               loc[STARLET1] + 1, loc[STARLET2] + 1,
404                               loc[DROID1] + 1, loc[DROID2] + 1,
405                               loc[LUSER1] + 1, loc[LUSER2] + 1);
406             }
407 #endif /* DEBUG */
408             else
409             {
410                 PM("Available commands are:\n");
411                 PM("  ?            --- print long instructions.");
412                 PM("  m <number>   --- move to room with given number.");
413                 PM("  t <numbers>  --- throw through given rooms.");
414 #ifdef DEBUG
415                 PM("  d            --- dump hazard locations.");
416 #endif /* DEBUG */
417                 PM("The list of room numbers after t must be space-separated");
418             }
419
420             (void) putchar('\n');
421         }
422
423         if (getlet("Play again") != 'y')
424         {
425             PM("Happy hacking!");
426             break;
427         }
428     }
429     return 0;
430 }
431
432 /* superhack.c ends here */