xref: /openbsd/games/wump/wump.c (revision ff8320a7)
1 /*	$OpenBSD: wump.c,v 1.23 2004/07/10 07:26:24 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 1989, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Dave Taylor, of Intuitive Systems.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #ifndef lint
37 static const char copyright[] =
38 "@(#) Copyright (c) 1989, 1993\n\
39 	The Regents of the University of California.  All rights reserved.\n";
40 #endif /* not lint */
41 
42 #ifndef lint
43 #if 0
44 static char sccsid[] = "@(#)wump.c	8.1 (Berkeley) 5/31/93";
45 #else
46 static const char rcsid[] = "$OpenBSD: wump.c,v 1.23 2004/07/10 07:26:24 deraadt Exp $";
47 #endif
48 #endif /* not lint */
49 
50 /*
51  * A no longer new version of the age-old favorite Hunt-The-Wumpus game that
52  * has been a part of the BSD distribution for longer than us old folk
53  * would care to remember.
54  */
55 
56 #include <sys/types.h>
57 #include <sys/wait.h>
58 #include <err.h>
59 #include <fcntl.h>
60 #include <paths.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <time.h>
65 #include <unistd.h>
66 #include "pathnames.h"
67 
68 /* some defines to spec out what our wumpus cave should look like */
69 
70 /* #define	MAX_ARROW_SHOT_DISTANCE	6	*/	/* +1 for '0' stopper */
71 #define	MAX_LINKS_IN_ROOM	25		/* a complex cave */
72 
73 #define	MAX_ROOMS_IN_CAVE	250
74 #define	ROOMS_IN_CAVE		20
75 #define	MIN_ROOMS_IN_CAVE	10
76 
77 #define	LINKS_IN_ROOM		3
78 #define	NUMBER_OF_ARROWS	5
79 #define	PIT_COUNT		3
80 #define	BAT_COUNT		3
81 
82 #define	EASY			1		/* levels of play */
83 #define	HARD			2
84 
85 /* some macro definitions for cleaner output */
86 
87 #define	plural(n)	(n == 1 ? "" : "s")
88 
89 /* simple cave data structure; +1 so we can index from '1' not '0' */
90 struct room_record {
91 	int tunnel[MAX_LINKS_IN_ROOM];
92 	int has_a_pit, has_a_bat;
93 } cave[MAX_ROOMS_IN_CAVE+1];
94 
95 /*
96  * global variables so we can keep track of where the player is, how
97  * many arrows they still have, where el wumpo is, and so on...
98  */
99 int player_loc = -1;			/* player location */
100 int wumpus_loc = -1;			/* The Bad Guy location */
101 int level = EASY;			/* level of play */
102 int arrows_left;			/* arrows unshot */
103 int oldstyle = 0;			/* dodecahedral cave? */
104 
105 #ifdef DEBUG
106 int debug = 0;
107 #endif
108 
109 int pit_num = -1;		/* # pits in cave */
110 int bat_num = -1;		/* # bats */
111 int room_num = ROOMS_IN_CAVE;		/* # rooms in cave */
112 int link_num = LINKS_IN_ROOM;		/* links per room  */
113 int arrow_num = NUMBER_OF_ARROWS;	/* arrow inventory */
114 
115 char answer[20];			/* user input */
116 
117 int	bats_nearby(void);
118 void	cave_init(void);
119 void	clear_things_in_cave(void);
120 void	display_room_stats(void);
121 void	dodecahedral_cave_init(void);
122 int	gcd(int, int);
123 int	getans(const char *);
124 void	initialize_things_in_cave(void);
125 void	instructions(void);
126 int	int_compare(const void *, const void *);
127 /* void	jump(int); */
128 void	kill_wump(void);
129 int	main(int, char **);
130 int	move_to(const char *);
131 void	move_wump(void);
132 void	no_arrows(void);
133 void	pit_kill(void);
134 void	pit_kill_bat(void);
135 int	pit_nearby(void);
136 void	pit_survive(void);
137 int	shoot(char *);
138 void	shoot_self(void);
139 int	take_action(void);
140 void	usage(void);
141 void	wump_kill(void);
142 void	wump_bat_kill(void);
143 void	wump_walk_kill(void);
144 int	wump_nearby(void);
145 
146 
147 int
148 main(int argc, char *argv[])
149 {
150 	int c;
151 
152 #ifdef DEBUG
153 	while ((c = getopt(argc, argv, "a:b:hop:r:t:d")) != -1)
154 #else
155 	while ((c = getopt(argc, argv, "a:b:hop:r:t:")) != -1)
156 #endif
157 		switch (c) {
158 		case 'a':
159 			arrow_num = atoi(optarg);
160 			break;
161 		case 'b':
162 			bat_num = atoi(optarg);
163 			break;
164 #ifdef DEBUG
165 		case 'd':
166 			debug = 1;
167 			break;
168 #endif
169 		case 'h':
170 			level = HARD;
171 			break;
172 		case 'o':
173 			oldstyle = 1;
174 			break;
175 		case 'p':
176 			pit_num = atoi(optarg);
177 			break;
178 		case 'r':
179 			room_num = atoi(optarg);
180 			if (room_num < MIN_ROOMS_IN_CAVE)
181 				errx(1,
182 	"no self-respecting wumpus would live in such a small cave!");
183 			if (room_num > MAX_ROOMS_IN_CAVE)
184 				errx(1,
185 	"even wumpii can't furnish caves that large!");
186 			break;
187 		case 't':
188 			link_num = atoi(optarg);
189 			if (link_num < 2)
190 				errx(1,
191 	"wumpii like extra doors in their caves!");
192 			break;
193 		case '?':
194 		default:
195 			usage();
196 	}
197 
198 	if (oldstyle) {
199 		room_num = 20;
200 		link_num = 3;
201 		/* Original game had exactly 2 bats and 2 pits */
202 		if (bat_num < 0)
203 			bat_num = 2;
204 		if (pit_num < 0)
205 			pit_num = 2;
206 	} else {
207 		if (bat_num < 0)
208 			bat_num = BAT_COUNT;
209 		if (pit_num < 0)
210 			pit_num = PIT_COUNT;
211 	}
212 
213 	if (link_num > MAX_LINKS_IN_ROOM ||
214 	    link_num > room_num - (room_num / 4))
215 		errx(1,
216 "too many tunnels!  The cave collapsed!\n(Fortunately, the wumpus escaped!)");
217 
218 	srandomdev();
219 	if (level == HARD) {
220 		if (room_num / 2 - bat_num)
221 			bat_num += (random() % (room_num / 2 - bat_num));
222 		if (room_num / 2 - pit_num)
223 			pit_num += (random() % (room_num / 2 - pit_num));
224 	}
225 
226 	/* Leave at least two rooms free--one for the player to start in, and
227 	 * potentially one for the wumpus.
228 	 */
229 	if (bat_num > room_num / 2 - 1)
230 		errx(1,
231 "the wumpus refused to enter the cave, claiming it was too crowded!");
232 
233 	if (pit_num > room_num / 2 - 1)
234 		errx(1,
235 "the wumpus refused to enter the cave, claiming it was too dangerous!");
236 
237 	instructions();
238 	if (oldstyle)
239 		dodecahedral_cave_init();
240 	else
241 		cave_init();
242 
243 	/* and we're OFF!  da dum, da dum, da dum, da dum... */
244 	(void)printf(
245 "\nYou're in a cave with %d rooms and %d tunnels leading from each room.\n\
246 There are %d bat%s and %d pit%s scattered throughout the cave, and your\n\
247 quiver holds %d custom super anti-evil Wumpus arrows.  Good luck.\n",
248 	    room_num, link_num, bat_num, plural(bat_num), pit_num,
249 	    plural(pit_num), arrow_num);
250 
251 	for (;;) {
252 		initialize_things_in_cave();
253 		arrows_left = arrow_num;
254 		do {
255 			display_room_stats();
256 			(void)printf("Move or shoot? (m-s) ");
257 			(void)fflush(stdout);
258 			(void)fpurge(stdin);
259 			if (!fgets(answer, sizeof(answer), stdin))
260 				break;
261 		} while (!take_action());
262 		(void)fpurge(stdin);
263 
264 		if (!getans("\nCare to play another game? (y-n) ")) {
265 			(void)printf("\n");
266 			exit(0);
267 		}
268 		clear_things_in_cave();
269 		if (!getans("In the same cave? (y-n) ")) {
270 			if (oldstyle)
271 				dodecahedral_cave_init();
272 			else
273 				cave_init();
274 		}
275 	}
276 	/* NOTREACHED */
277 }
278 
279 void
280 display_room_stats(void)
281 {
282 	int i;
283 
284 	/*
285 	 * Routine will explain what's going on with the current room, as well
286 	 * as describe whether there are pits, bats, & wumpii nearby.  It's
287 	 * all pretty mindless, really.
288 	 */
289 	(void)printf(
290 "\nYou are in room %d of the cave, and have %d arrow%s left.\n",
291 	    player_loc, arrows_left, plural(arrows_left));
292 
293 	if (bats_nearby())
294 		(void)printf("*rustle* *rustle* (must be bats nearby)\n");
295 	if (pit_nearby())
296 		(void)printf("*whoosh* (I feel a draft from some pits).\n");
297 	if (wump_nearby())
298 		(void)printf("*sniff* (I can smell the evil Wumpus nearby!)\n");
299 
300 	(void)printf("There are tunnels to rooms %d, ",
301 	   cave[player_loc].tunnel[0]);
302 
303 	for (i = 1; i < link_num - 1; i++)
304 /*		if (cave[player_loc].tunnel[i] <= room_num) */
305 			(void)printf("%d, ", cave[player_loc].tunnel[i]);
306 	(void)printf("and %d.\n", cave[player_loc].tunnel[link_num - 1]);
307 }
308 
309 int
310 take_action(void)
311 {
312 	/*
313 	 * Do the action specified by the player, either 'm'ove, 's'hoot
314 	 * or something exceptionally bizarre and strange!  Returns 1
315 	 * iff the player died during this turn, otherwise returns 0.
316 	 */
317 	switch (*answer) {
318 		case 'M':
319 		case 'm':			/* move */
320 			return(move_to(answer + 1));
321 		case 'S':
322 		case 's':			/* shoot */
323 			return(shoot(answer + 1));
324 		case 'Q':
325 		case 'q':
326 		case 'x':
327 			exit(0);
328 		case '\n':
329 			return(0);
330 		}
331 	if (random() % 15 == 1)
332 		(void)printf("Que pasa?\n");
333 	else
334 		(void)printf("I don't understand!\n");
335 	return(0);
336 }
337 
338 int
339 move_to(const char *room_number)
340 {
341 	int i, just_moved_by_bats, next_room, tunnel_available;
342 
343 	/*
344 	 * This is responsible for moving the player into another room in the
345 	 * cave as per their directions.  If room_number is a null string,
346 	 * then we'll prompt the user for the next room to go into.   Once
347 	 * we've moved into the room, we'll check for things like bats, pits,
348 	 * and so on.  This routine returns 1 if something occurs that kills
349 	 * the player and 0 otherwise...
350 	 */
351 	tunnel_available = just_moved_by_bats = 0;
352 	next_room = atoi(room_number);
353 
354 	/* crap for magic tunnels */
355 /*	if (next_room == room_num + 1 &&
356  *	    cave[player_loc].tunnel[link_num-1] != next_room)
357  *		++next_room;
358  */
359 	while (next_room < 1 || next_room > room_num /* + 1 */) {
360 		if (next_room < 0 && next_room != -1)
361 (void)printf("Sorry, but we're constrained to a semi-Euclidean cave!\n");
362 		if (next_room > room_num /* + 1 */)
363 (void)printf("What?  The cave surely isn't quite that big!\n");
364 /*		if (next_room == room_num + 1 &&
365  *		    cave[player_loc].tunnel[link_num-1] != next_room) {
366  *			(void)printf("What?  The cave isn't that big!\n");
367  *			++next_room;
368  *		}
369  */		(void)printf("To which room do you wish to move? ");
370 		(void)fflush(stdout);
371 		if (!fgets(answer, sizeof(answer), stdin))
372 			return(1);
373 		next_room = atoi(answer);
374 	}
375 
376 	/* now let's see if we can move to that room or not */
377 	tunnel_available = 0;
378 	for (i = 0; i < link_num; i++)
379 		if (cave[player_loc].tunnel[i] == next_room)
380 			tunnel_available = 1;
381 
382 	if (!tunnel_available) {
383 		(void)printf("*Oof!*  (You hit the wall)\n");
384 		if (random() % 6 == 1) {
385 (void)printf("Your colorful comments awaken the wumpus!\n");
386 			move_wump();
387 			if (wumpus_loc == player_loc) {
388 				wump_walk_kill();
389 				return(1);
390 			}
391 		}
392 		return(0);
393 	}
394 
395 	/* now let's move into that room and check it out for dangers */
396 /*	if (next_room == room_num + 1)
397  *		jump(next_room = (random() % room_num) + 1);
398  */
399 	player_loc = next_room;
400 	for (;;) {
401 		if (next_room == wumpus_loc) {		/* uh oh... */
402 			if (just_moved_by_bats)
403 				wump_bat_kill();
404 			else
405 				wump_kill();
406 			return(1);
407 		}
408 		if (cave[next_room].has_a_pit) {
409 			if (random() % 12 < 2) {
410 				pit_survive();
411 				return(0);
412 			} else {
413 				if (just_moved_by_bats)
414 					pit_kill_bat();
415 				else
416 					pit_kill();
417 				return(1);
418 			}
419 		}
420 
421 		if (cave[next_room].has_a_bat) {
422 			(void)printf(
423 "*flap*  *flap*  *flap*  (humongous bats pick you up and move you%s!)\n",
424 			    just_moved_by_bats ? " again": "");
425 			next_room = player_loc = (random() % room_num) + 1;
426 			just_moved_by_bats = 1;
427 		}
428 
429 		else
430 			break;
431 	}
432 	return(0);
433 }
434 
435 int
436 shoot(char *room_list)
437 {
438 	int chance, next, roomcnt;
439 	int j, arrow_location, link, ok;
440 	char *p;
441 
442 	/*
443 	 * Implement shooting arrows.  Arrows are shot by the player indicating
444 	 * a space-separated list of rooms that the arrow should pass through;
445 	 * if any of the rooms they specify are not accessible via tunnel from
446 	 * the room the arrow is in, it will instead fly randomly into another
447 	 * room.  If the player hits the wumpus, this routine will indicate
448 	 * such.  If it misses, this routine may *move* the wumpus one room.
449 	 * If it's the last arrow, then the player dies...  Returns 1 if the
450 	 * player has won or died, 0 if nothing has happened.
451 	 */
452 	arrow_location = player_loc;
453 	for (roomcnt = 1;; ++roomcnt, room_list = NULL) {
454 		if (!(p = strtok(room_list, " \t\n"))) {
455 			if (roomcnt == 1) {
456 				(void)printf("Enter a list of rooms to shoot into:\n");
457 				(void)fflush(stdout);
458 				if (!(p = strtok(fgets(answer, sizeof(answer), stdin),
459 							" \t\n"))) {
460 					(void)printf(
461 				"The arrow falls to the ground at your feet.\n");
462 					return(0);
463 					}
464 			} else
465 				break;
466 		}
467 		if (roomcnt > 5) {
468 			(void)printf(
469 "The arrow wavers in its flight and can go no further than room %d!\n",
470 					arrow_location);
471 			break;
472 		}
473 
474 		next = atoi(p);
475 		if (next == 0)
476 			break;	/* Old wumpus used room 0 as the terminator */
477 
478 		chance = random() % 10;
479 		if (roomcnt == 4 && chance < 2) {
480 			(void)printf(
481 "Your finger slips on the bowstring!  *twaaaaaang*\n\
482 The arrow is weakly shot and can go no further than room %d!\n",arrow_location);
483 			break;
484 		} else if (roomcnt == 5 && chance < 6) {
485 			(void)printf(
486 "The arrow wavers in its flight and can go no further than room %d!\n",
487 					arrow_location);
488 			break;
489 		}
490 
491 		for (j = 0, ok = 0; j < link_num; j++)
492 			if (cave[arrow_location].tunnel[j] == next)
493 				ok = 1;
494 
495 		if (ok) {
496 /*			if (next > room_num) {
497  *				(void)printf(
498  * "A faint gleam tells you the arrow has gone through a magic tunnel!\n");
499  *				arrow_location = (random() % room_num) + 1;
500  *			} else
501  */				arrow_location = next;
502 		} else {
503 			link = (random() % link_num);
504 			if (cave[arrow_location].tunnel[link] == player_loc)
505 				(void)printf(
506 "*thunk*  The arrow can't find a way from %d to %d and flies back into\n\
507 your room!\n",
508 				    arrow_location, next);
509 /*			else if (cave[arrow_location].tunnel[link] > room_num)
510  *				(void)printf(
511  *"*thunk*  The arrow flies randomly into a magic tunnel, thence into\n\
512  *room %d!\n",
513  *				    cave[arrow_location].tunnel[link]);
514  */			else
515 				(void)printf(
516 "*thunk*  The arrow can't find a way from %d to %d and flies randomly\n\
517 into room %d!\n", arrow_location, next, cave[arrow_location].tunnel[link]);
518 
519 			arrow_location = cave[arrow_location].tunnel[link];
520 		}
521 
522 		/*
523 		 * now we've gotten into the new room let us see if El Wumpo is
524 		 * in the same room ... if so we've a HIT and the player WON!
525 		 */
526 		if (arrow_location == wumpus_loc) {
527 			kill_wump();
528 			return(1);
529 		}
530 
531 		if (arrow_location == player_loc) {
532 			shoot_self();
533 			return(1);
534 		}
535 	}
536 
537 	if (!--arrows_left) {
538 		no_arrows();
539 		return(1);
540 	}
541 
542 	{
543 		/* each time you shoot, it's more likely the wumpus moves */
544 		static int lastchance = 2;
545 
546 		if (random() % level == EASY ? 12 : 9 < (lastchance += 2)) {
547 			move_wump();
548 			if (wumpus_loc == player_loc) {
549 				wump_walk_kill();
550 				lastchance = random() % 3;   /* Reset for next game */
551 				return(1);
552 			}
553 
554 		}
555 	}
556 	(void)printf("The arrow hit nothing.\n");
557 	return(0);
558 }
559 
560 int
561 gcd(int a, int b)
562 {
563 	int r;
564 
565 	if (!(r = (a % b)))
566 		return(b);
567 	return(gcd(b, r));
568 }
569 
570 void
571 cave_init(void)
572 {
573 	int i, j, k, link;
574 	int delta;
575 
576 	/*
577 	 * This does most of the interesting work in this program actually!
578 	 * In this routine we'll initialize the Wumpus cave to have all rooms
579 	 * linking to all others by stepping through our data structure once,
580 	 * recording all forward links and backwards links too.  The parallel
581 	 * "linkcount" data structure ensures that no room ends up with more
582 	 * than three links, regardless of the quality of the random number
583 	 * generator that we're using.
584 	 */
585 
586 	/* Note that throughout the source there are commented-out vestigial
587 	 * remains of the 'magic tunnel', which was a tunnel to room
588 	 * room_num +1.  It was necessary if all paths were two-way and
589 	 * there was an odd number of rooms, each with an odd number of
590 	 * exits.  It's being kept in case cave_init ever gets reworked into
591 	 * something more traditional.
592 	 */
593 
594 	/* initialize the cave first off. */
595 	for (i = 1; i <= room_num; ++i)
596 		for (j = 0; j < link_num ; ++j)
597 			cave[i].tunnel[j] = -1;
598 
599 	/* choose a random 'hop' delta for our guaranteed link.
600 	 * To keep the cave connected, require greatest common
601 	 * divisor of (delta + 1) and room_num to be 1
602 	 */
603 	do {
604 		delta = (random() % (room_num - 1)) + 1;
605 	} while (gcd(room_num, delta + 1) != 1);
606 
607 	for (i = 1; i <= room_num; ++i) {
608 		link = ((i + delta) % room_num) + 1;	/* connection */
609 		cave[i].tunnel[0] = link;		/* forw link */
610 		cave[link].tunnel[1] = i;		/* back link */
611 	}
612 	/* now fill in the rest of the cave with random connections.
613 	 * This is a departure from historical versions of wumpus.
614 	 */
615 	for (i = 1; i <= room_num; i++)
616 		for (j = 2; j < link_num ; j++) {
617 			if (cave[i].tunnel[j] != -1)
618 				continue;
619 try_again:		link = (random() % room_num) + 1;
620 			/* skip duplicates */
621 			for (k = 0; k < j; k++)
622 				if (cave[i].tunnel[k] == link)
623 					goto try_again;
624 			/* don't let a room connect to itself */
625 			if (link == i)
626 				goto try_again;
627 			cave[i].tunnel[j] = link;
628 			if (random() % 2 == 1)
629 				continue;
630 			for (k = 0; k < link_num; ++k) {
631 				/* if duplicate, skip it */
632 				if (cave[link].tunnel[k] == i)
633 					k = link_num;
634 				else {
635 					/* if open link, use it, force exit */
636 					if (cave[link].tunnel[k] == -1) {
637 						cave[link].tunnel[k] = i;
638 						k = link_num;
639 					}
640 				}
641 			}
642 		}
643 	/*
644 	 * now that we're done, sort the tunnels in each of the rooms to
645 	 * make it easier on the intrepid adventurer.
646 	 */
647 	for (i = 1; i <= room_num; ++i)
648 		qsort(cave[i].tunnel, (u_int)link_num,
649 		    sizeof(cave[i].tunnel[0]), int_compare);
650 
651 #ifdef DEBUG
652 	if (debug)
653 		for (i = 1; i <= room_num; ++i) {
654 			(void)printf("<room %d  has tunnels to ", i);
655 			for (j = 0; j < link_num; ++j)
656 				(void)printf("%d ", cave[i].tunnel[j]);
657 			(void)printf(">\n");
658 		}
659 #endif
660 }
661 
662 void
663 dodecahedral_cave_init(void)
664 {
665 	int vert[20][3] = {
666 		{1, 4, 7},
667 		{0, 2, 9},
668 		{1, 3, 11},
669 		{2, 4, 13},
670 		{0, 3, 5},
671 		{4, 6, 14},
672 		{5, 7, 16},
673 		{0, 6, 8},
674 		{7, 9, 17},
675 		{1, 8, 10},
676 		{9, 11, 18},
677 		{2, 10, 12},
678 		{11, 13, 19},
679 		{3, 12, 14},
680 		{5, 13, 15},
681 		{14, 16, 19},
682 		{6, 15, 17},
683 		{8, 16, 18},
684 		{10, 17, 19},
685 		{12, 15, 18},
686 	};
687 	int loc[20];
688 	int i, j, temp;
689 
690 	if (room_num != 20 || link_num != 3)
691 		errx(1, "wrong parameters for dodecahedron");
692 	for (i = 0; i < 20; i++)
693 		loc[i] = i;
694 	for (i = 0; i < 20; i++) {
695 		j = random() % (20 - i);
696 		if (j) {
697 			temp = loc[i];
698 			loc[i] = loc[i + j];
699 			loc[i + j] = temp;
700 		}
701 	}
702 	/* cave is offset by 1 */
703 	for (i = 0; i < 20; i++) {
704 		for (j = 0; j < 3; j++)
705 			cave[loc[i] + 1].tunnel[j] = loc[vert[i][j]] + 1;
706 	}
707 
708 	/*
709 	 * now that we're done, sort the tunnels in each of the rooms to
710 	 * make it easier on the intrepid adventurer.
711 	 */
712 	for (i = 1; i <= room_num; ++i)
713 		qsort(cave[i].tunnel, (u_int)link_num,
714 		    sizeof(cave[i].tunnel[0]), int_compare);
715 
716 #ifdef DEBUG
717 	if (debug)
718 		for (i = 1; i <= room_num; ++i) {
719 			(void)printf("<room %d  has tunnels to ", i);
720 			for (j = 0; j < link_num; ++j)
721 				(void)printf("%d ", cave[i].tunnel[j]);
722 			(void)printf(">\n");
723 		}
724 #endif
725 }
726 
727 void
728 clear_things_in_cave(void)
729 {
730 	int i;
731 
732 	/*
733 	 * remove bats and pits from the current cave in preparation for us
734 	 * adding new ones via the initialize_things_in_cave() routines.
735 	 */
736 	for (i = 1; i <= room_num; ++i)
737 		cave[i].has_a_bat = cave[i].has_a_pit = 0;
738 }
739 
740 void
741 initialize_things_in_cave(void)
742 {
743 	int i, loc;
744 
745 	/* place some bats, pits, the wumpus, and the player. */
746 	for (i = 0; i < bat_num; ++i) {
747 		do {
748 			loc = (random() % room_num) + 1;
749 		} while (cave[loc].has_a_bat);
750 		cave[loc].has_a_bat = 1;
751 #ifdef DEBUG
752 		if (debug)
753 			(void)printf("<bat in room %d>\n", loc);
754 #endif
755 	}
756 
757 	for (i = 0; i < pit_num; ++i) {
758 		do {
759 			loc = (random() % room_num) + 1;
760 		} while (cave[loc].has_a_pit || cave[loc].has_a_bat);
761 		/* Above used to be &&;  || makes sense but so does just
762 		 * checking cave[loc].has_a_pit  */
763 		cave[loc].has_a_pit = 1;
764 #ifdef DEBUG
765 		if (debug)
766 			(void)printf("<pit in room %d>\n", loc);
767 #endif
768 	}
769 
770 	wumpus_loc = (random() % room_num) + 1;
771 #ifdef DEBUG
772 	if (debug)
773 		(void)printf("<wumpus in room %d>\n", wumpus_loc);
774 #endif
775 
776 	do {
777 		player_loc = (random() % room_num) + 1;
778 	} while (player_loc == wumpus_loc || cave[player_loc].has_a_pit ||
779 			cave[player_loc].has_a_bat);
780 	/* Replaced (level == HARD ?
781 	 *  (link_num / room_num < 0.4 ? wump_nearby() : 0) : 0)
782 	 * with bat/pit checks in initial room.  If this is kept there is
783 	 * a slight chance that no room satisfies all four conditions.
784 	 */
785 }
786 
787 int
788 getans(const char *prompt)
789 {
790 	char buf[20];
791 
792 	/*
793 	 * simple routine to ask the yes/no question specified until the user
794 	 * answers yes or no, then return 1 if they said 'yes' and 0 if they
795 	 * answered 'no'.
796 	 */
797 	for (;;) {
798 		(void)printf("%s", prompt);
799 		(void)fflush(stdout);
800 		if (!fgets(buf, sizeof(buf), stdin))
801 			return(0);
802 		if (*buf == 'N' || *buf == 'n')
803 			return(0);
804 		if (*buf == 'Y' || *buf == 'y')
805 			return(1);
806 		(void)printf(
807 "I don't understand your answer; please enter 'y' or 'n'!\n");
808 	}
809 	/* NOTREACHED */
810 }
811 
812 int
813 bats_nearby(void)
814 {
815 	int i;
816 
817 	/* check for bats in the immediate vicinity */
818 	for (i = 0; i < link_num; ++i)
819 		if (cave[cave[player_loc].tunnel[i]].has_a_bat)
820 			return(1);
821 	return(0);
822 }
823 
824 int
825 pit_nearby(void)
826 {
827 	int i;
828 
829 	/* check for pits in the immediate vicinity */
830 	for (i = 0; i < link_num; ++i)
831 		if (cave[cave[player_loc].tunnel[i]].has_a_pit)
832 			return(1);
833 	return(0);
834 }
835 
836 int
837 wump_nearby(void)
838 {
839 	int i, j;
840 
841 	/* check for a wumpus within TWO caves of where we are */
842 	for (i = 0; i < link_num; ++i) {
843 		if (cave[player_loc].tunnel[i] == wumpus_loc)
844 			return(1);
845 		for (j = 0; j < link_num; ++j)
846 			if (cave[cave[player_loc].tunnel[i]].tunnel[j] ==
847 			    wumpus_loc)
848 				return(1);
849 	}
850 	return(0);
851 }
852 
853 void
854 move_wump(void)
855 {
856 	wumpus_loc = cave[wumpus_loc].tunnel[random() % link_num];
857 #ifdef DEBUG
858 	if (debug)
859 		(void)printf("Wumpus moved to room %d\n",wumpus_loc);
860 #endif
861 }
862 
863 int
864 int_compare(const void *a, const void *b)
865 {
866 	return(*(const int *)a < *(const int *)b ? -1 : 1);
867 }
868 
869 void
870 instructions(void)
871 {
872 	const char *pager;
873 	pid_t pid;
874 	int status;
875 	int fd;
876 
877 	/*
878 	 * read the instructions file, if needed, and show the user how to
879 	 * play this game!
880 	 */
881 	if (!getans("Instructions? (y-n) "))
882 		return;
883 
884 	if ((fd = open(_PATH_WUMPINFO, O_RDONLY)) == -1) {
885 		(void)printf(
886 "Sorry, but the instruction file seems to have disappeared in a\n\
887 puff of greasy black smoke! (poof)\n");
888 		return;
889 	}
890 
891 	if (!isatty(1))
892 		pager = "/bin/cat";
893 	else {
894 		if (!(pager = getenv("PAGER")) || (*pager == 0))
895 			pager = _PATH_PAGER;
896 	}
897 	switch (pid = fork()) {
898 	case 0: /* child */
899 		if (dup2(fd, 0) == -1)
900 			err(1, "dup2");
901 		(void)execl(_PATH_BSHELL, "sh", "-c", pager, (char *)NULL);
902 		err(1, "exec sh -c %s", pager);
903 		/* NOT REACHED */
904 	case -1:
905 		err(1, "fork");
906 		/* NOT REACHED */
907 	default:
908 		(void)waitpid(pid, &status, 0);
909 		close(fd);
910 		break;
911 	}
912 }
913 
914 void
915 usage(void)
916 {
917 	(void)fprintf(stderr,
918 "usage: wump [-ho] [-a arrows] [-b bats] [-p pits] [-r rooms] [-t tunnels]\n");
919 	exit(1);
920 }
921 
922 /* messages */
923 void
924 wump_kill(void)
925 {
926 	(void)printf(
927 "*ROAR* *chomp* *snurfle* *chomp*!\n\
928 Much to the delight of the Wumpus, you walk right into his mouth,\n\
929 making you one of the easiest dinners he's ever had!  For you, however,\n\
930 it's a rather unpleasant death.  The only good thing is that it's been\n\
931 so long since the evil Wumpus cleaned his teeth that you immediately\n\
932 pass out from the stench!\n");
933 }
934 
935 void
936 wump_walk_kill(void)
937 {
938 	(void)printf(
939 "Oh dear.  All the commotion has managed to awaken the evil Wumpus, who\n\
940 has chosen to walk into this very room!  Your eyes open wide as they behold\n\
941 the great sucker-footed bulk that is the Wumpus; the mouth of the Wumpus\n\
942 also opens wide as the evil beast beholds dinner.\n\
943 *ROAR* *chomp* *snurfle* *chomp*!\n");
944 }
945 
946 void
947 wump_bat_kill(void)
948 {
949 	(void)printf(
950 "Flap, flap.  The bats fly you right into the room with the evil Wumpus!\n\
951 The Wumpus, seeing a fine dinner flying overhead, takes a swipe at you,\n\
952 and the bats, not wanting to serve as hors d'oeuvres, drop their\n\
953 soon-to-be-dead weight and take off in the way that only bats flying out\n\
954 of a very bad place can.  As you fall towards the large, sharp, and very\n\
955 foul-smelling teeth of the Wumpus, you think, \"Man, this is going to hurt.\"\n\
956 It does.\n");
957 }
958 
959 void
960 kill_wump(void)
961 {
962 	(void)printf(
963 "*thwock!* *groan* *crash*\n\n\
964 A horrible roar fills the cave, and you realize, with a smile, that you\n\
965 have slain the evil Wumpus and won the game!  You don't want to tarry for\n\
966 long, however, because not only is the Wumpus famous, but the stench of\n\
967 dead Wumpus is also quite well known--a stench powerful enough to slay the\n\
968 mightiest adventurer at a single whiff!!\n");
969 }
970 
971 void
972 no_arrows(void)
973 {
974 	(void)printf(
975 "\nYou turn and look at your quiver, and realize with a sinking feeling\n\
976 that you've just shot your last arrow (figuratively, too).  Sensing this\n\
977 with its psychic powers, the evil Wumpus rampages through the cave, finds\n\
978 you, and with a mighty *ROAR* eats you alive!\n");
979 }
980 
981 void
982 shoot_self(void)
983 {
984 	(void)printf(
985 "\n*Thwack!*  A sudden piercing feeling informs you that your wild arrow\n\
986 has ricocheted back and wedged in your side, causing extreme agony.  The\n\
987 evil Wumpus, with its psychic powers, realizes this and immediately rushes\n\
988 to your side, not to help, alas, but to EAT YOU!\n\
989 (*CHOMP*)\n");
990 }
991 
992 /*
993  * void
994  * jump(int where)
995  * {
996  * 	(void)printf(
997  * "\nWith a jaunty step you enter the magic tunnel.  As you do, you\n\
998  * notice that the walls are shimmering and glowing.  Suddenly you feel\n\
999  * a very curious, warm sensation and find yourself in room %d!!\n", where);
1000  * }
1001  */
1002 
1003 void
1004 pit_kill(void)
1005 {
1006 	(void)printf(
1007 "*AAAUUUUGGGGGHHHHHhhhhhhhhhh...*\n\
1008 The whistling sound and updraft as you walked into this room of the\n\
1009 cave apparently weren't enough to clue you in to the presence of the\n\
1010 bottomless pit.  You have a lot of time to reflect on this error as\n\
1011 you fall many miles to the core of the earth.  Look on the bright side;\n\
1012 you can at least find out if Jules Verne was right...\n");
1013 }
1014 
1015 void
1016 pit_kill_bat(void)
1017 {
1018 	(void)printf(
1019 "*AAAUUUUGGGGGHHHHHhhhhhhhhhh...*\n\
1020 It appears the bats have decided to drop you into a bottomless pit.  At\n\
1021 least, that's what the whistling sound and updraft would suggest.  Look on\n\
1022 the bright side; you can at least find out if Jules Verne was right...\n");
1023 }
1024 
1025 void
1026 pit_survive(void)
1027 {
1028 	(void)printf(
1029 "Without conscious thought you grab for the side of the cave and manage\n\
1030 to grasp onto a rocky outcrop.  Beneath your feet stretches the limitless\n\
1031 depths of a bottomless pit!  Rock crumbles beneath your feet!\n");
1032 }
1033