xref: /original-bsd/games/wump/wump.c (revision 1897046e)
184689157Sbostic /*
2*1897046eSbostic  * Copyright (c) 1989, 1993
3*1897046eSbostic  *	The Regents of the University of California.  All rights reserved.
484689157Sbostic  * All rights reserved.
584689157Sbostic  *
684689157Sbostic  * This code is derived from software contributed to Berkeley by
784689157Sbostic  * Dave Taylor, of Intuitive Systems.
884689157Sbostic  *
9593194efSbostic  * %sccs.include.redist.c%
1084689157Sbostic  */
11957a0273Smckusick 
1284689157Sbostic #ifndef lint
13*1897046eSbostic static char copyright[] =
14*1897046eSbostic "@(#) Copyright (c) 1989, 1993\n\
15*1897046eSbostic 	The Regents of the University of California.  All rights reserved.\n";
1684689157Sbostic #endif /* not lint */
17957a0273Smckusick 
1884689157Sbostic #ifndef lint
19*1897046eSbostic static char sccsid[] = "@(#)wump.c	8.1 (Berkeley) 05/31/93";
2084689157Sbostic #endif /* not lint */
2184689157Sbostic 
2284689157Sbostic /*
2384689157Sbostic  * A very new version of the age old favorite Hunt-The-Wumpus game that has
2484689157Sbostic  * been a part of the BSD distribution of Unix for longer than us old folk
2584689157Sbostic  * would care to remember.
2684689157Sbostic  */
2784689157Sbostic 
2884689157Sbostic #include <sys/types.h>
2984689157Sbostic #include <sys/file.h>
30957a0273Smckusick #include <stdio.h>
3184689157Sbostic #include "pathnames.h"
3284689157Sbostic 
3384689157Sbostic /* some defines to spec out what our wumpus cave should look like */
3484689157Sbostic 
3584689157Sbostic #define	MAX_ARROW_SHOT_DISTANCE	6		/* +1 for '0' stopper */
3684689157Sbostic #define	MAX_LINKS_IN_ROOM	25		/* a complex cave */
3784689157Sbostic 
3884689157Sbostic #define	MAX_ROOMS_IN_CAVE	250
3984689157Sbostic #define	ROOMS_IN_CAVE		20
4084689157Sbostic #define	MIN_ROOMS_IN_CAVE	10
4184689157Sbostic 
4284689157Sbostic #define	LINKS_IN_ROOM		3
4384689157Sbostic #define	NUMBER_OF_ARROWS	5
4484689157Sbostic #define	PIT_COUNT		3
4584689157Sbostic #define	BAT_COUNT		3
4684689157Sbostic 
4784689157Sbostic #define	EASY			1		/* levels of play */
4884689157Sbostic #define	HARD			2
4984689157Sbostic 
5084689157Sbostic /* some macro definitions for cleaner output */
5184689157Sbostic 
5284689157Sbostic #define	plural(n)	(n == 1 ? "" : "s")
5384689157Sbostic 
5484689157Sbostic /* simple cave data structure; +1 so we can index from '1' not '0' */
5584689157Sbostic struct room_record {
5684689157Sbostic 	int tunnel[MAX_LINKS_IN_ROOM];
5784689157Sbostic 	int has_a_pit, has_a_bat;
5884689157Sbostic } cave[MAX_ROOMS_IN_CAVE+1];
59957a0273Smckusick 
60957a0273Smckusick /*
6184689157Sbostic  * global variables so we can keep track of where the player is, how
6284689157Sbostic  * many arrows they still have, where el wumpo is, and so on...
63957a0273Smckusick  */
6484689157Sbostic int player_loc = -1;			/* player location */
6584689157Sbostic int wumpus_loc = -1;			/* The Bad Guy location */
6684689157Sbostic int level = EASY;			/* level of play */
6784689157Sbostic int arrows_left;			/* arrows unshot */
68957a0273Smckusick 
6984689157Sbostic #ifdef DEBUG
7084689157Sbostic int debug = 0;
7184689157Sbostic #endif
72957a0273Smckusick 
7384689157Sbostic int pit_num = PIT_COUNT;		/* # pits in cave */
7484689157Sbostic int bat_num = BAT_COUNT;		/* # bats */
7584689157Sbostic int room_num = ROOMS_IN_CAVE;		/* # rooms in cave */
7684689157Sbostic int link_num = LINKS_IN_ROOM;		/* links per room  */
7784689157Sbostic int arrow_num = NUMBER_OF_ARROWS;	/* arrow inventory */
7884689157Sbostic 
7984689157Sbostic char answer[20];			/* user input */
8084689157Sbostic 
main(argc,argv)8184689157Sbostic main(argc, argv)
8284689157Sbostic 	int argc;
8384689157Sbostic 	char **argv;
84957a0273Smckusick {
8584689157Sbostic 	extern char *optarg;
86957a0273Smckusick 	int c;
87957a0273Smckusick 
8884689157Sbostic #ifdef DEBUG
8984689157Sbostic 	while ((c = getopt(argc, argv, "a:b:hp:r:t:d")) != EOF)
9084689157Sbostic #else
9184689157Sbostic 	while ((c = getopt(argc, argv, "a:b:hp:r:t:")) != EOF)
9284689157Sbostic #endif
9384689157Sbostic 		switch (c) {
9484689157Sbostic 		case 'a':
9584689157Sbostic 			arrow_num = atoi(optarg);
9684689157Sbostic 			break;
9784689157Sbostic 		case 'b':
9884689157Sbostic 			bat_num = atoi(optarg);
9984689157Sbostic 			break;
10084689157Sbostic #ifdef DEBUG
10184689157Sbostic 		case 'd':
10284689157Sbostic 			debug = 1;
10384689157Sbostic 			break;
10484689157Sbostic #endif
10584689157Sbostic 		case 'h':
10684689157Sbostic 			level = HARD;
10784689157Sbostic 			break;
10884689157Sbostic 		case 'p':
10984689157Sbostic 			pit_num = atoi(optarg);
11084689157Sbostic 			break;
11184689157Sbostic 		case 'r':
11284689157Sbostic 			room_num = atoi(optarg);
11384689157Sbostic 			if (room_num < MIN_ROOMS_IN_CAVE) {
11484689157Sbostic 				(void)fprintf(stderr,
11584689157Sbostic 	"No self-respecting wumpus would live in such a small cave!\n");
11684689157Sbostic 				exit(1);
117957a0273Smckusick 			}
11884689157Sbostic 			if (room_num > MAX_ROOMS_IN_CAVE) {
11984689157Sbostic 				(void)fprintf(stderr,
12084689157Sbostic 	"Even wumpii can't furnish caves that large!\n");
12184689157Sbostic 				exit(1);
12284689157Sbostic 			}
12384689157Sbostic 			break;
12484689157Sbostic 		case 't':
12584689157Sbostic 			link_num = atoi(optarg);
12684689157Sbostic 			if (link_num < 2) {
12784689157Sbostic 				(void)fprintf(stderr,
12884689157Sbostic 	"Wumpii like extra doors in their caves!\n");
12984689157Sbostic 				exit(1);
13084689157Sbostic 			}
13184689157Sbostic 			break;
13284689157Sbostic 		case '?':
13384689157Sbostic 		default:
13484689157Sbostic 			usage();
135957a0273Smckusick 	}
136957a0273Smckusick 
13784689157Sbostic 	if (link_num > MAX_LINKS_IN_ROOM ||
13884689157Sbostic 	    link_num > room_num - (room_num / 4)) {
13984689157Sbostic 		(void)fprintf(stderr,
14084689157Sbostic "Too many tunnels!  The cave collapsed!\n(Fortunately, the wumpus escaped!)\n");
14184689157Sbostic 		exit(1);
14284689157Sbostic 	}
14384689157Sbostic 
14484689157Sbostic 	if (level == HARD) {
14584689157Sbostic 		bat_num += ((random() % (room_num / 2)) + 1);
14684689157Sbostic 		pit_num += ((random() % (room_num / 2)) + 1);
14784689157Sbostic 	}
14884689157Sbostic 
14984689157Sbostic 	if (bat_num > room_num / 2) {
15084689157Sbostic 		(void)fprintf(stderr,
15184689157Sbostic "The wumpus refused to enter the cave, claiming it was too crowded!\n");
15284689157Sbostic 		exit(1);
15384689157Sbostic 	}
15484689157Sbostic 
15584689157Sbostic 	if (pit_num > room_num / 2) {
15684689157Sbostic 		(void)fprintf(stderr,
15784689157Sbostic "The wumpus refused to enter the cave, claiming it was too dangerous!\n");
15884689157Sbostic 		exit(1);
15984689157Sbostic 	}
16084689157Sbostic 
16184689157Sbostic 	instructions();
16284689157Sbostic 	cave_init();
16384689157Sbostic 
16484689157Sbostic 	/* and we're OFF!  da dum, da dum, da dum, da dum... */
16584689157Sbostic 	(void)printf(
16684689157Sbostic "\nYou're in a cave with %d rooms and %d tunnels leading from each room.\n\
16784689157Sbostic There are %d bat%s and %d pit%s scattered throughout the cave, and your\n\
16884689157Sbostic quiver holds %d custom super anti-evil Wumpus arrows.  Good luck.\n",
16984689157Sbostic 	    room_num, link_num, bat_num, plural(bat_num), pit_num,
17084689157Sbostic 	    plural(pit_num), arrow_num);
17184689157Sbostic 
17284689157Sbostic 	for (;;) {
17384689157Sbostic 		initialize_things_in_cave();
17484689157Sbostic 		arrows_left = arrow_num;
17584689157Sbostic 		do {
17684689157Sbostic 			display_room_stats();
17784689157Sbostic 			(void)printf("Move or shoot? (m-s) ");
17884689157Sbostic 			(void)fflush(stdout);
17984689157Sbostic 			if (!fgets(answer, sizeof(answer), stdin))
18084689157Sbostic 				break;
18184689157Sbostic 		} while (!take_action());
18284689157Sbostic 
18384689157Sbostic 		if (!getans("\nCare to play another game? (y-n) "))
18484689157Sbostic 			exit(0);
18584689157Sbostic 		if (getans("In the same cave? (y-n) "))
18684689157Sbostic 			clear_things_in_cave();
18784689157Sbostic 		else
18884689157Sbostic 			cave_init();
18984689157Sbostic 	}
19084689157Sbostic 	/* NOTREACHED */
19184689157Sbostic }
19284689157Sbostic 
display_room_stats()19384689157Sbostic display_room_stats()
194957a0273Smckusick {
19584689157Sbostic 	register int i;
196957a0273Smckusick 
19784689157Sbostic 	/*
19884689157Sbostic 	 * Routine will explain what's going on with the current room, as well
19984689157Sbostic 	 * as describe whether there are pits, bats, & wumpii nearby.  It's
20084689157Sbostic 	 * all pretty mindless, really.
20184689157Sbostic 	 */
20284689157Sbostic 	(void)printf(
20384689157Sbostic "\nYou are in room %d of the cave, and have %d arrow%s left.\n",
20484689157Sbostic 	    player_loc, arrows_left, plural(arrows_left));
20584689157Sbostic 
20684689157Sbostic 	if (bats_nearby())
20784689157Sbostic 		(void)printf("*rustle* *rustle* (must be bats nearby)\n");
20884689157Sbostic 	if (pit_nearby())
20984689157Sbostic 		(void)printf("*whoosh* (I feel a draft from some pits).\n");
21084689157Sbostic 	if (wump_nearby())
21184689157Sbostic 		(void)printf("*sniff* (I can smell the evil Wumpus nearby!)\n");
21284689157Sbostic 
21384689157Sbostic 	(void)printf("There are tunnels to rooms %d, ",
21484689157Sbostic 	   cave[player_loc].tunnel[0]);
21584689157Sbostic 
21684689157Sbostic 	for (i = 1; i < link_num - 1; i++)
21784689157Sbostic 		if (cave[player_loc].tunnel[i] <= room_num)
21884689157Sbostic 			(void)printf("%d, ", cave[player_loc].tunnel[i]);
21984689157Sbostic 	(void)printf("and %d.\n", cave[player_loc].tunnel[link_num - 1]);
220957a0273Smckusick }
221957a0273Smckusick 
take_action()22284689157Sbostic take_action()
223957a0273Smckusick {
22484689157Sbostic 	/*
22584689157Sbostic 	 * Do the action specified by the player, either 'm'ove, 's'hoot
22684689157Sbostic 	 * or something exceptionally bizarre and strange!  Returns 1
22784689157Sbostic 	 * iff the player died during this turn, otherwise returns 0.
22884689157Sbostic 	 */
22984689157Sbostic 	switch (*answer) {
23084689157Sbostic 		case 'M':
23184689157Sbostic 		case 'm':			/* move */
23284689157Sbostic 			return(move_to(answer + 1));
23384689157Sbostic 		case 'S':
23484689157Sbostic 		case 's':			/* shoot */
23584689157Sbostic 			return(shoot(answer + 1));
23684689157Sbostic 		case 'Q':
23784689157Sbostic 		case 'q':
23884689157Sbostic 		case 'x':
23984689157Sbostic 			exit(0);
24084689157Sbostic 		case '\n':
24184689157Sbostic 			return(0);
242957a0273Smckusick 		}
24384689157Sbostic 	if (random() % 15 == 1)
24484689157Sbostic 		(void)printf("Que pasa?\n");
24584689157Sbostic 	else
24684689157Sbostic 		(void)printf("I don't understand!\n");
24784689157Sbostic 	return(0);
248957a0273Smckusick }
249957a0273Smckusick 
move_to(room_number)25084689157Sbostic move_to(room_number)
25184689157Sbostic 	char *room_number;
252957a0273Smckusick {
25384689157Sbostic 	int i, just_moved_by_bats, next_room, tunnel_available;
254957a0273Smckusick 
25584689157Sbostic 	/*
25684689157Sbostic 	 * This is responsible for moving the player into another room in the
25784689157Sbostic 	 * cave as per their directions.  If room_number is a null string,
25884689157Sbostic 	 * then we'll prompt the user for the next room to go into.   Once
25984689157Sbostic 	 * we've moved into the room, we'll check for things like bats, pits,
26084689157Sbostic 	 * and so on.  This routine returns 1 if something occurs that kills
26184689157Sbostic 	 * the player and 0 otherwise...
26284689157Sbostic 	 */
26384689157Sbostic 	tunnel_available = just_moved_by_bats = 0;
26484689157Sbostic 	next_room = atoi(room_number);
26584689157Sbostic 
26684689157Sbostic 	/* crap for magic tunnels */
26784689157Sbostic 	if (next_room == room_num + 1 &&
26884689157Sbostic 	    cave[player_loc].tunnel[link_num-1] != next_room)
26984689157Sbostic 		++next_room;
27084689157Sbostic 
27184689157Sbostic 	while (next_room < 1 || next_room > room_num + 1) {
27284689157Sbostic 		if (next_room < 0 && next_room != -1)
27384689157Sbostic (void)printf("Sorry, but we're constrained to a semi-Euclidean cave!\n");
27484689157Sbostic 		if (next_room > room_num + 1)
27584689157Sbostic (void)printf("What?  The cave surely isn't quite that big!\n");
27684689157Sbostic 		if (next_room == room_num + 1 &&
27784689157Sbostic 		    cave[player_loc].tunnel[link_num-1] != next_room) {
27884689157Sbostic 			(void)printf("What?  The cave isn't that big!\n");
27984689157Sbostic 			++next_room;
28084689157Sbostic 		}
28184689157Sbostic 		(void)printf("To which room do you wish to move? ");
28284689157Sbostic 		(void)fflush(stdout);
28384689157Sbostic 		if (!fgets(answer, sizeof(answer), stdin))
28484689157Sbostic 			return(1);
28584689157Sbostic 		next_room = atoi(answer);
28684689157Sbostic 	}
28784689157Sbostic 
28884689157Sbostic 	/* now let's see if we can move to that room or not */
28984689157Sbostic 	tunnel_available = 0;
29084689157Sbostic 	for (i = 0; i < link_num; i++)
29184689157Sbostic 		if (cave[player_loc].tunnel[i] == next_room)
29284689157Sbostic 			tunnel_available = 1;
29384689157Sbostic 
29484689157Sbostic 	if (!tunnel_available) {
29584689157Sbostic 		(void)printf("*Oof!*  (You hit the wall)\n");
29684689157Sbostic 		if (random() % 6 == 1) {
29784689157Sbostic (void)printf("Your colorful comments awaken the wumpus!\n");
29884689157Sbostic 			move_wump();
29984689157Sbostic 			if (wumpus_loc == player_loc) {
30084689157Sbostic 				wump_kill();
30184689157Sbostic 				return(1);
30284689157Sbostic 			}
303957a0273Smckusick 		}
304957a0273Smckusick 		return(0);
305957a0273Smckusick 	}
30684689157Sbostic 
30784689157Sbostic 	/* now let's move into that room and check it out for dangers */
30884689157Sbostic 	if (next_room == room_num + 1)
30984689157Sbostic 		jump(next_room = (random() % room_num) + 1);
31084689157Sbostic 
31184689157Sbostic 	player_loc = next_room;
31284689157Sbostic 	for (;;) {
31384689157Sbostic 		if (next_room == wumpus_loc) {		/* uh oh... */
31484689157Sbostic 			wump_kill();
31584689157Sbostic 			return(1);
316957a0273Smckusick 		}
31784689157Sbostic 		if (cave[next_room].has_a_pit)
31884689157Sbostic 			if (random() % 12 < 2) {
31984689157Sbostic 				pit_survive();
32084689157Sbostic 				return(0);
32184689157Sbostic 			} else {
32284689157Sbostic 				pit_kill();
32384689157Sbostic 				return(1);
324957a0273Smckusick 			}
325957a0273Smckusick 
32684689157Sbostic 		if (cave[next_room].has_a_bat) {
32784689157Sbostic 			(void)printf(
32884689157Sbostic "*flap*  *flap*  *flap*  (humongous bats pick you up and move you%s!)\n",
32984689157Sbostic 			    just_moved_by_bats ? " again": "");
33084689157Sbostic 			next_room = player_loc = (random() % room_num) + 1;
33184689157Sbostic 			just_moved_by_bats = 1;
33284689157Sbostic 		}
33384689157Sbostic 
33484689157Sbostic 		else
33584689157Sbostic 			break;
33684689157Sbostic 	}
33784689157Sbostic 	return(0);
33884689157Sbostic }
33984689157Sbostic 
shoot(room_list)34084689157Sbostic shoot(room_list)
34184689157Sbostic 	char *room_list;
342957a0273Smckusick {
34384689157Sbostic 	int chance, next, roomcnt;
34484689157Sbostic 	int j, arrow_location, link, ok;
34584689157Sbostic 	char *p, *strtok();
346957a0273Smckusick 
34784689157Sbostic 	/*
34884689157Sbostic 	 * Implement shooting arrows.  Arrows are shot by the player indicating
34984689157Sbostic 	 * a space-separated list of rooms that the arrow should pass through;
35084689157Sbostic 	 * if any of the rooms they specify are not accessible via tunnel from
35184689157Sbostic 	 * the room the arrow is in, it will instead fly randomly into another
35284689157Sbostic 	 * room.  If the player hits the wumpus, this routine will indicate
35384689157Sbostic 	 * such.  If it misses, this routine will *move* the wumpus one room.
35484689157Sbostic 	 * If it's the last arrow, the player then dies...  Returns 1 if the
35584689157Sbostic 	 * player has won or died, 0 if nothing has happened.
35684689157Sbostic 	 */
35784689157Sbostic 	arrow_location = player_loc;
35884689157Sbostic 	for (roomcnt = 1;; ++roomcnt, room_list = NULL) {
35984689157Sbostic 		if (!(p = strtok(room_list, " \t\n")))
36084689157Sbostic 			if (roomcnt == 1) {
36184689157Sbostic 				(void)printf(
36284689157Sbostic 			"The arrow falls to the ground at your feet!\n");
36384689157Sbostic 				return(0);
36484689157Sbostic 			} else
36584689157Sbostic 				break;
36684689157Sbostic 		if (roomcnt > 5) {
36784689157Sbostic 			(void)printf(
36884689157Sbostic "The arrow wavers in its flight and and can go no further!\n");
36984689157Sbostic 			break;
37084689157Sbostic 		}
37184689157Sbostic 		next = atoi(p);
37284689157Sbostic 		for (j = 0, ok = 0; j < link_num; j++)
37384689157Sbostic 			if (cave[arrow_location].tunnel[j] == next)
37484689157Sbostic 				ok = 1;
37584689157Sbostic 
37684689157Sbostic 		if (ok) {
37784689157Sbostic 			if (next > room_num) {
37884689157Sbostic 				(void)printf(
37984689157Sbostic "A faint gleam tells you the arrow has gone through a magic tunnel!\n");
38084689157Sbostic 				arrow_location = (random() % room_num) + 1;
38184689157Sbostic 			} else
38284689157Sbostic 				arrow_location = next;
38384689157Sbostic 		} else {
38484689157Sbostic 			link = (random() % link_num);
38584689157Sbostic 			if (link == player_loc)
38684689157Sbostic 				(void)printf(
38784689157Sbostic "*thunk*  The arrow can't find a way from %d to %d and flys back into\n\
38884689157Sbostic your room!\n",
38984689157Sbostic 				    arrow_location, next);
39084689157Sbostic 			else if (cave[arrow_location].tunnel[link] > room_num)
39184689157Sbostic 				(void)printf(
39284689157Sbostic "*thunk*  The arrow flys randomly into a magic tunnel, thence into\n\
39384689157Sbostic room %d!\n",
39484689157Sbostic 				    cave[arrow_location].tunnel[link]);
39584689157Sbostic 			else
39684689157Sbostic 				(void)printf(
39784689157Sbostic "*thunk*  The arrow can't find a way from %d to %d and flys randomly\n\
39884689157Sbostic into room %d!\n",
39984689157Sbostic 				    arrow_location, next,
40084689157Sbostic 				    cave[arrow_location].tunnel[link]);
40184689157Sbostic 			arrow_location = cave[arrow_location].tunnel[link];
40284689157Sbostic 			break;
40384689157Sbostic 		}
40484689157Sbostic 		chance = random() % 10;
40584689157Sbostic 		if (roomcnt == 3 && chance < 2) {
40684689157Sbostic 			(void)printf(
40784689157Sbostic "Your bowstring breaks!  *twaaaaaang*\n\
40884689157Sbostic The arrow is weakly shot and can go no further!\n");
40984689157Sbostic 			break;
41084689157Sbostic 		} else if (roomcnt == 4 && chance < 6) {
41184689157Sbostic 			(void)printf(
41284689157Sbostic "The arrow wavers in its flight and and can go no further!\n");
41384689157Sbostic 			break;
41484689157Sbostic 		}
41584689157Sbostic 	}
41684689157Sbostic 
41784689157Sbostic 	/*
41884689157Sbostic 	 * now we've gotten into the new room let us see if El Wumpo is
41984689157Sbostic 	 * in the same room ... if so we've a HIT and the player WON!
42084689157Sbostic 	 */
42184689157Sbostic 	if (arrow_location == wumpus_loc) {
42284689157Sbostic 		kill_wump();
42384689157Sbostic 		return(1);
42484689157Sbostic 	}
42584689157Sbostic 
42684689157Sbostic 	if (arrow_location == player_loc) {
42784689157Sbostic 		shoot_self();
42884689157Sbostic 		return(1);
42984689157Sbostic 	}
43084689157Sbostic 
43184689157Sbostic 	if (!--arrows_left) {
43284689157Sbostic 		no_arrows();
43384689157Sbostic 		return(1);
43484689157Sbostic 	}
43584689157Sbostic 
43684689157Sbostic 	{
43784689157Sbostic 		/* each time you shoot, it's more likely the wumpus moves */
43884689157Sbostic 		static int lastchance = 2;
43984689157Sbostic 
44084689157Sbostic 		if (random() % level == EASY ? 12 : 9 < (lastchance += 2)) {
44184689157Sbostic 			move_wump();
44284689157Sbostic 			if (wumpus_loc == player_loc)
44384689157Sbostic 				wump_kill();
44484689157Sbostic 			lastchance = random() % 3;
44584689157Sbostic 
44684689157Sbostic 		}
44784689157Sbostic 	}
44884689157Sbostic 	return(0);
44984689157Sbostic }
45084689157Sbostic 
cave_init()45184689157Sbostic cave_init()
45284689157Sbostic {
45384689157Sbostic 	register int i, j, k, link;
45484689157Sbostic 	int delta, int_compare();
45584689157Sbostic 	time_t time();
45684689157Sbostic 
45784689157Sbostic 	/*
45884689157Sbostic 	 * This does most of the interesting work in this program actually!
45984689157Sbostic 	 * In this routine we'll initialize the Wumpus cave to have all rooms
46084689157Sbostic 	 * linking to all others by stepping through our data structure once,
46184689157Sbostic 	 * recording all forward links and backwards links too.  The parallel
46284689157Sbostic 	 * "linkcount" data structure ensures that no room ends up with more
46384689157Sbostic 	 * than three links, regardless of the quality of the random number
46484689157Sbostic 	 * generator that we're using.
46584689157Sbostic 	 */
46684689157Sbostic 	srandom((int)time((time_t *)0));
46784689157Sbostic 
46884689157Sbostic 	/* initialize the cave first off. */
46984689157Sbostic 	for (i = 1; i <= room_num; ++i)
47084689157Sbostic 		for (j = 0; j < link_num ; ++j)
47184689157Sbostic 			cave[i].tunnel[j] = -1;
47284689157Sbostic 
47384689157Sbostic 	/* choose a random 'hop' delta for our guaranteed link */
47484689157Sbostic 	while (!(delta = random() % room_num));
47584689157Sbostic 
47684689157Sbostic 	for (i = 1; i <= room_num; ++i) {
47784689157Sbostic 		link = ((i + delta) % room_num) + 1;	/* connection */
47884689157Sbostic 		cave[i].tunnel[0] = link;		/* forw link */
47984689157Sbostic 		cave[link].tunnel[1] = i;		/* back link */
48084689157Sbostic 	}
48184689157Sbostic 	/* now fill in the rest of the cave with random connections */
48284689157Sbostic 	for (i = 1; i <= room_num; i++)
48384689157Sbostic 		for (j = 2; j < link_num ; j++) {
48484689157Sbostic 			if (cave[i].tunnel[j] != -1)
48584689157Sbostic 				continue;
48684689157Sbostic try_again:		link = (random() % room_num) + 1;
48784689157Sbostic 			/* skip duplicates */
48884689157Sbostic 			for (k = 0; k < j; k++)
48984689157Sbostic 				if (cave[i].tunnel[k] == link)
49084689157Sbostic 					goto try_again;
49184689157Sbostic 			cave[i].tunnel[j] = link;
49284689157Sbostic 			if (random() % 2 == 1)
49384689157Sbostic 				continue;
49484689157Sbostic 			for (k = 0; k < link_num; ++k) {
49584689157Sbostic 				/* if duplicate, skip it */
49684689157Sbostic 				if (cave[link].tunnel[k] == i)
49784689157Sbostic 					k = link_num;
49884689157Sbostic 
49984689157Sbostic 				/* if open link, use it, force exit */
50084689157Sbostic 				if (cave[link].tunnel[k] == -1) {
50184689157Sbostic 					cave[link].tunnel[k] = i;
50284689157Sbostic 					k = link_num;
50384689157Sbostic 				}
50484689157Sbostic 			}
50584689157Sbostic 		}
50684689157Sbostic 	/*
50784689157Sbostic 	 * now that we're done, sort the tunnels in each of the rooms to
50884689157Sbostic 	 * make it easier on the intrepid adventurer.
50984689157Sbostic 	 */
51084689157Sbostic 	for (i = 1; i <= room_num; ++i)
51184689157Sbostic 		qsort(cave[i].tunnel, (u_int)link_num,
51284689157Sbostic 		    sizeof(cave[i].tunnel[0]), int_compare);
51384689157Sbostic 
51484689157Sbostic #ifdef DEBUG
51584689157Sbostic 	if (debug)
51684689157Sbostic 		for (i = 1; i <= room_num; ++i) {
51784689157Sbostic 			(void)printf("<room %d  has tunnels to ", i);
51884689157Sbostic 			for (j = 0; j < link_num; ++j)
51984689157Sbostic 				(void)printf("%d ", cave[i].tunnel[j]);
52084689157Sbostic 			(void)printf(">\n");
52184689157Sbostic 		}
52284689157Sbostic #endif
52384689157Sbostic }
52484689157Sbostic 
clear_things_in_cave()52584689157Sbostic clear_things_in_cave()
52684689157Sbostic {
52784689157Sbostic 	register int i;
52884689157Sbostic 
52984689157Sbostic 	/*
53084689157Sbostic 	 * remove bats and pits from the current cave in preparation for us
53184689157Sbostic 	 * adding new ones via the initialize_things_in_cave() routines.
53284689157Sbostic 	 */
53384689157Sbostic 	for (i = 1; i <= room_num; ++i)
53484689157Sbostic 		cave[i].has_a_bat = cave[i].has_a_pit = 0;
53584689157Sbostic }
53684689157Sbostic 
initialize_things_in_cave()53784689157Sbostic initialize_things_in_cave()
53884689157Sbostic {
53984689157Sbostic 	register int i, loc;
54084689157Sbostic 
54184689157Sbostic 	/* place some bats, pits, the wumpus, and the player. */
54284689157Sbostic 	for (i = 0; i < bat_num; ++i) {
54384689157Sbostic 		do {
54484689157Sbostic 			loc = (random() % room_num) + 1;
54584689157Sbostic 		} while (cave[loc].has_a_bat);
54684689157Sbostic 		cave[loc].has_a_bat = 1;
54784689157Sbostic #ifdef DEBUG
54884689157Sbostic 		if (debug)
54984689157Sbostic 			(void)printf("<bat in room %d>\n", loc);
55084689157Sbostic #endif
55184689157Sbostic 	}
55284689157Sbostic 
55384689157Sbostic 	for (i = 0; i < pit_num; ++i) {
55484689157Sbostic 		do {
55584689157Sbostic 			loc = (random() % room_num) + 1;
55684689157Sbostic 		} while (cave[loc].has_a_pit && cave[loc].has_a_bat);
55784689157Sbostic 		cave[loc].has_a_pit = 1;
55884689157Sbostic #ifdef DEBUG
55984689157Sbostic 		if (debug)
56084689157Sbostic 			(void)printf("<pit in room %d>\n", loc);
56184689157Sbostic #endif
56284689157Sbostic 	}
56384689157Sbostic 
56484689157Sbostic 	wumpus_loc = (random() % room_num) + 1;
56584689157Sbostic #ifdef DEBUG
56684689157Sbostic 	if (debug)
56784689157Sbostic 		(void)printf("<wumpus in room %d>\n", loc);
56884689157Sbostic #endif
56984689157Sbostic 
57084689157Sbostic 	do {
57184689157Sbostic 		player_loc = (random() % room_num) + 1;
57284689157Sbostic 	} while (player_loc == wumpus_loc || (level == HARD ?
57384689157Sbostic 	    (link_num / room_num < 0.4 ? wump_nearby() : 0) : 0));
57484689157Sbostic }
57584689157Sbostic 
getans(prompt)57684689157Sbostic getans(prompt)
57784689157Sbostic 	char *prompt;
57884689157Sbostic {
57984689157Sbostic 	char buf[20];
58084689157Sbostic 
58184689157Sbostic 	/*
58284689157Sbostic 	 * simple routine to ask the yes/no question specified until the user
58384689157Sbostic 	 * answers yes or no, then return 1 if they said 'yes' and 0 if they
58484689157Sbostic 	 * answered 'no'.
58584689157Sbostic 	 */
58684689157Sbostic 	for (;;) {
58784689157Sbostic 		(void)printf("%s", prompt);
58884689157Sbostic 		(void)fflush(stdout);
58984689157Sbostic 		if (!fgets(buf, sizeof(buf), stdin))
59084689157Sbostic 			return(0);
59184689157Sbostic 		if (*buf == 'N' || *buf == 'n')
59284689157Sbostic 			return(0);
59384689157Sbostic 		if (*buf == 'Y' || *buf == 'y')
59484689157Sbostic 			return(1);
59584689157Sbostic 		(void)printf(
59684689157Sbostic "I don't understand your answer; please enter 'y' or 'n'!\n");
59784689157Sbostic 	}
59884689157Sbostic 	/* NOTREACHED */
59984689157Sbostic }
60084689157Sbostic 
bats_nearby()60184689157Sbostic bats_nearby()
60284689157Sbostic {
60384689157Sbostic 	register int i;
60484689157Sbostic 
60584689157Sbostic 	/* check for bats in the immediate vicinity */
60684689157Sbostic 	for (i = 0; i < link_num; ++i)
60784689157Sbostic 		if (cave[cave[player_loc].tunnel[i]].has_a_bat)
608957a0273Smckusick 			return(1);
609957a0273Smckusick 	return(0);
610957a0273Smckusick }
611957a0273Smckusick 
pit_nearby()61284689157Sbostic pit_nearby()
613957a0273Smckusick {
61484689157Sbostic 	register int i;
615957a0273Smckusick 
61684689157Sbostic 	/* check for pits in the immediate vicinity */
61784689157Sbostic 	for (i = 0; i < link_num; ++i)
61884689157Sbostic 		if (cave[cave[player_loc].tunnel[i]].has_a_pit)
61984689157Sbostic 			return(1);
62084689157Sbostic 	return(0);
621957a0273Smckusick }
622957a0273Smckusick 
wump_nearby()62384689157Sbostic wump_nearby()
62484689157Sbostic {
62584689157Sbostic 	register int i, j;
62684689157Sbostic 
62784689157Sbostic 	/* check for a wumpus within TWO caves of where we are */
62884689157Sbostic 	for (i = 0; i < link_num; ++i) {
62984689157Sbostic 		if (cave[player_loc].tunnel[i] == wumpus_loc)
63084689157Sbostic 			return(1);
63184689157Sbostic 		for (j = 0; j < link_num; ++j)
63284689157Sbostic 			if (cave[cave[player_loc].tunnel[i]].tunnel[j] ==
63384689157Sbostic 			    wumpus_loc)
63484689157Sbostic 				return(1);
63584689157Sbostic 	}
63684689157Sbostic 	return(0);
63784689157Sbostic }
63884689157Sbostic 
move_wump()63984689157Sbostic move_wump()
64084689157Sbostic {
64184689157Sbostic 	wumpus_loc = cave[wumpus_loc].tunnel[random() % link_num];
64284689157Sbostic }
64384689157Sbostic 
int_compare(a,b)64484689157Sbostic int_compare(a, b)
64584689157Sbostic 	int *a, *b;
64684689157Sbostic {
64784689157Sbostic 	return(*a < *b ? -1 : 1);
64884689157Sbostic }
64984689157Sbostic 
instructions()65084689157Sbostic instructions()
65184689157Sbostic {
65284689157Sbostic 	char buf[120], *p, *getenv();
65384689157Sbostic 
65484689157Sbostic 	/*
65584689157Sbostic 	 * read the instructions file, if needed, and show the user how to
65684689157Sbostic 	 * play this game!
65784689157Sbostic 	 */
65884689157Sbostic 	if (!getans("Instructions? (y-n) "))
65984689157Sbostic 		return;
66084689157Sbostic 
66184689157Sbostic 	if (access(_PATH_WUMPINFO, R_OK)) {
66284689157Sbostic 		(void)printf(
66384689157Sbostic "Sorry, but the instruction file seems to have disappeared in a\n\
66484689157Sbostic puff of greasy black smoke! (poof)\n");
66584689157Sbostic 		return;
66684689157Sbostic 	}
66784689157Sbostic 
66884689157Sbostic 	if (!(p = getenv("PAGER")) ||
66984689157Sbostic 	    strlen(p) > sizeof(buf) + strlen(_PATH_WUMPINFO) + 5)
67084689157Sbostic 		p = _PATH_PAGER;
67184689157Sbostic 
67284689157Sbostic 	(void)sprintf(buf, "%s %s", p, _PATH_WUMPINFO);
67384689157Sbostic 	(void)system(buf);
67484689157Sbostic }
67584689157Sbostic 
usage()67684689157Sbostic usage()
67784689157Sbostic {
67884689157Sbostic 	(void)fprintf(stderr,
67984689157Sbostic "usage: wump [-h] [-a arrows] [-b bats] [-p pits] [-r rooms] [-t tunnels]\n");
68084689157Sbostic 	exit(1);
68184689157Sbostic }
68284689157Sbostic 
68384689157Sbostic /* messages */
68484689157Sbostic 
wump_kill()68584689157Sbostic wump_kill()
68684689157Sbostic {
68784689157Sbostic 	(void)printf(
68884689157Sbostic "*ROAR* *chomp* *snurfle* *chomp*!\n\
68984689157Sbostic Much to the delight of the Wumpus, you walked right into his mouth,\n\
69084689157Sbostic making you one of the easiest dinners he's ever had!  For you, however,\n\
69184689157Sbostic it's a rather unpleasant death.  The only good thing is that it's been\n\
69284689157Sbostic so long since the evil Wumpus cleaned his teeth that you immediately\n\
69384689157Sbostic passed out from the stench!\n");
69484689157Sbostic }
69584689157Sbostic 
kill_wump()69684689157Sbostic kill_wump()
69784689157Sbostic {
69884689157Sbostic 	(void)printf(
69984689157Sbostic "*thwock!* *groan* *crash*\n\n\
70084689157Sbostic A horrible roar fills the cave, and you realize, with a smile, that you\n\
70184689157Sbostic have slain the evil Wumpus and won the game!  You don't want to tarry for\n\
70284689157Sbostic long, however, because not only is the Wumpus famous, but the stench of\n\
70384689157Sbostic dead Wumpus is also quite well known, a stench plenty enough to slay the\n\
70484689157Sbostic mightiest adventurer at a single whiff!!\n");
70584689157Sbostic }
70684689157Sbostic 
no_arrows()70784689157Sbostic no_arrows()
70884689157Sbostic {
70984689157Sbostic 	(void)printf(
71084689157Sbostic "\nYou turn and look at your quiver, and realize with a sinking feeling\n\
71184689157Sbostic that you've just shot your last arrow (figuratively, too).  Sensing this\n\
71284689157Sbostic with its psychic powers, the evil Wumpus rampagees through the cave, finds\n\
71384689157Sbostic you, and with a mighty *ROAR* eats you alive!\n");
71484689157Sbostic }
71584689157Sbostic 
shoot_self()71684689157Sbostic shoot_self()
71784689157Sbostic {
71884689157Sbostic 	(void)printf(
71984689157Sbostic "\n*Thwack!*  A sudden piercing feeling informs you that the ricochet\n\
72084689157Sbostic of your wild arrow has resulted in it wedging in your side, causing\n\
72184689157Sbostic extreme agony.  The evil Wumpus, with its psychic powers, realizes this\n\
72284689157Sbostic and immediately rushes to your side, not to help, alas, but to EAT YOU!\n\
72384689157Sbostic (*CHOMP*)\n");
72484689157Sbostic }
72584689157Sbostic 
jump(where)72684689157Sbostic jump(where)
72784689157Sbostic 	int where;
72884689157Sbostic {
72984689157Sbostic 	(void)printf(
73084689157Sbostic "\nWith a jaunty step you enter the magic tunnel.  As you do, you\n\
73184689157Sbostic notice that the walls are shimmering and glowing.  Suddenly you feel\n\
73284689157Sbostic a very curious, warm sensation and find yourself in room %d!!\n", where);
73384689157Sbostic }
73484689157Sbostic 
pit_kill()73584689157Sbostic pit_kill()
73684689157Sbostic {
73784689157Sbostic 	(void)printf(
73884689157Sbostic "*AAAUUUUGGGGGHHHHHhhhhhhhhhh...*\n\
73984689157Sbostic The whistling sound and updraft as you walked into this room of the\n\
74084689157Sbostic cave apparently wasn't enough to clue you in to the presence of the\n\
74184689157Sbostic bottomless pit.  You have a lot of time to reflect on this error as\n\
74284689157Sbostic you fall many miles to the core of the earth.  Look on the bright side;\n\
74384689157Sbostic you can at least find out if Jules Verne was right...\n");
74484689157Sbostic }
74584689157Sbostic 
pit_survive()74684689157Sbostic pit_survive()
74784689157Sbostic {
74884689157Sbostic 	(void)printf(
74984689157Sbostic "Without conscious thought you grab for the side of the cave and manage\n\
75084689157Sbostic to grasp onto a rocky outcrop.  Beneath your feet stretches the limitless\n\
75184689157Sbostic depths of a bottomless pit!  Rock crumbles beneath your feet!\n");
752957a0273Smckusick }
753