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