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