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