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