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