+/* $NetBSD: wump.c,v 1.31 2021/05/02 12:50:47 rillig Exp $ */
+
/*
- * Copyright (c) 1989 The Regents of the University of California.
- * Copyright (c) 1989 Dave Taylor, Intuitive Systems.
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
+ * 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* SUCH DAMAGE.
*/
+#include <sys/cdefs.h>
#ifndef lint
-char copyright[] =
-"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
- All rights reserved.\n";
+__COPYRIGHT("@(#) Copyright (c) 1989, 1993\
+ The Regents of the University of California. All rights reserved.");
#endif /* not lint */
#ifndef lint
-static char sccsid[] = "@(#)wump.c 4.3 (Berkeley) 6/1/90";
+#if 0
+static char sccsid[] = "@(#)wump.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: wump.c,v 1.31 2021/05/02 12:50:47 rillig Exp $");
+#endif
#endif /* not lint */
/*
* would care to remember.
*/
+#include <err.h>
#include <sys/types.h>
#include <sys/file.h>
+#include <sys/wait.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
#include "pathnames.h"
/* some defines to spec out what our wumpus cave should look like */
#define plural(n) (n == 1 ? "" : "s")
/* simple cave data structure; +1 so we can index from '1' not '0' */
-struct room_record {
+static struct room_record {
int tunnel[MAX_LINKS_IN_ROOM];
int has_a_pit, has_a_bat;
} cave[MAX_ROOMS_IN_CAVE+1];
* global variables so we can keep track of where the player is, how
* many arrows they still have, where el wumpo is, and so on...
*/
-int player_loc = -1; /* player location */
-int wumpus_loc = -1; /* The Bad Guy location */
-int level = EASY; /* level of play */
-int arrows_left; /* arrows unshot */
+static int player_loc = -1; /* player location */
+static int wumpus_loc = -1; /* The Bad Guy location */
+static int level = EASY; /* level of play */
+static int arrows_left; /* arrows unshot */
#ifdef DEBUG
-int debug = 0;
+static int debug = 0;
#endif
-int pit_num = PIT_COUNT; /* # pits in cave */
-int bat_num = BAT_COUNT; /* # bats */
-int room_num = ROOMS_IN_CAVE; /* # rooms in cave */
-int link_num = LINKS_IN_ROOM; /* links per room */
-int arrow_num = NUMBER_OF_ARROWS; /* arrow inventory */
-
-char answer[20]; /* user input */
-
-main(argc, argv)
- int argc;
- char **argv;
+static int pit_num = PIT_COUNT; /* # pits in cave */
+static int bat_num = BAT_COUNT; /* # bats */
+static int room_num = ROOMS_IN_CAVE; /* # rooms in cave */
+static int link_num = LINKS_IN_ROOM; /* links per room */
+static int arrow_num = NUMBER_OF_ARROWS;/* arrow inventory */
+
+static char answer[20]; /* user input */
+
+int main(int, char **);
+static int bats_nearby(void);
+static void cave_init(void);
+static void clear_things_in_cave(void);
+static void display_room_stats(void);
+static int gcd(int, int);
+static int getans(const char *);
+static void initialize_things_in_cave(void);
+static void instructions(void);
+static int int_compare(const void *, const void *);
+static void jump(int);
+static void kill_wump(void);
+static int move_to(const char *);
+static void move_wump(void);
+static void no_arrows(void);
+static void pit_kill(void);
+static int pit_nearby(void);
+static void pit_survive(void);
+static int shoot(char *);
+static void shoot_self(void);
+static int take_action(void);
+static void usage(void) __dead;
+static void wump_kill(void);
+static int wump_nearby(void);
+
+int
+main(int argc, char **argv)
{
- extern char *optarg;
- int c;
+ int c, e=0;
+
+ /* Revoke setgid privileges */
+ setgid(getgid());
#ifdef DEBUG
- while ((c = getopt(argc, argv, "a:b:hp:r:t:d")) != EOF)
+ while ((c = getopt(argc, argv, "a:b:hp:r:t:d")) != -1)
#else
- while ((c = getopt(argc, argv, "a:b:hp:r:t:")) != EOF)
+ while ((c = getopt(argc, argv, "a:b:hp:r:t:")) != -1)
#endif
switch (c) {
case 'a':
}
if (room_num > MAX_ROOMS_IN_CAVE) {
(void)fprintf(stderr,
- "Even wumpii can't furnish caves that large!\n");
+ "Even wumpi can't furnish caves that large!\n");
exit(1);
}
break;
link_num = atoi(optarg);
if (link_num < 2) {
(void)fprintf(stderr,
- "Wumpii like extra doors in their caves!\n");
+ "Wumpi like extra doors in their caves!\n");
exit(1);
}
break;
plural(pit_num), arrow_num);
for (;;) {
+ clear_things_in_cave();
initialize_things_in_cave();
arrows_left = arrow_num;
do {
display_room_stats();
(void)printf("Move or shoot? (m-s) ");
(void)fflush(stdout);
- if (!fgets(answer, sizeof(answer), stdin))
+ if (!fgets(answer, sizeof(answer), stdin)) {
+ e=2;
break;
- } while (!take_action());
+ }
+ } while (!(e = take_action()));
- if (!getans("\nCare to play another game? (y-n) "))
+ if (e == 2 || !getans("\nCare to play another game? (y-n) "))
exit(0);
- if (getans("In the same cave? (y-n) "))
- clear_things_in_cave();
- else
+ if (getans("In the same cave? (y-n) ") == 0)
cave_init();
}
/* NOTREACHED */
+ return (0);
}
-display_room_stats()
+static void
+display_room_stats(void)
{
- register int i;
+ int i;
/*
* Routine will explain what's going on with the current room, as well
- * as describe whether there are pits, bats, & wumpii nearby. It's
+ * as describe whether there are pits, bats, & wumpi nearby. It's
* all pretty mindless, really.
*/
(void)printf(
(void)printf("and %d.\n", cave[player_loc].tunnel[link_num - 1]);
}
-take_action()
+static int
+take_action(void)
{
/*
* Do the action specified by the player, either 'm'ove, 's'hoot
return(0);
}
-move_to(room_number)
- char *room_number;
+static int
+move_to(const char *room_number)
{
int i, just_moved_by_bats, next_room, tunnel_available;
wump_kill();
return(1);
}
- if (cave[next_room].has_a_pit)
+ if (cave[next_room].has_a_pit) {
if (random() % 12 < 2) {
pit_survive();
return(0);
pit_kill();
return(1);
}
+ }
if (cave[next_room].has_a_bat) {
(void)printf(
return(0);
}
-shoot(room_list)
- char *room_list;
+static int
+shoot(char *room_list)
{
int chance, next, roomcnt;
- int j, arrow_location, link, ok;
- char *p, *strtok();
+ int j, arrow_location, lnk, ok;
+ char *p;
/*
* Implement shooting arrows. Arrows are shot by the player indicating
*/
arrow_location = player_loc;
for (roomcnt = 1;; ++roomcnt, room_list = NULL) {
- if (!(p = strtok(room_list, " \t\n")))
+ if (!(p = strtok(room_list, " \t\n"))) {
if (roomcnt == 1) {
(void)printf(
"The arrow falls to the ground at your feet!\n");
return(0);
} else
break;
+ }
if (roomcnt > 5) {
(void)printf(
"The arrow wavers in its flight and and can go no further!\n");
} else
arrow_location = next;
} else {
- link = (random() % link_num);
- if (link == player_loc)
+ lnk = (random() % link_num);
+ if (lnk == player_loc)
(void)printf(
-"*thunk* The arrow can't find a way from %d to %d and flys back into\n\
+"*thunk* The arrow can't find a way from %d to %d and flies back into\n\
your room!\n",
arrow_location, next);
- else if (cave[arrow_location].tunnel[link] > room_num)
+ else if (cave[arrow_location].tunnel[lnk] > room_num)
(void)printf(
-"*thunk* The arrow flys randomly into a magic tunnel, thence into\n\
+"*thunk* The arrow flies randomly into a magic tunnel, thence into\n\
room %d!\n",
- cave[arrow_location].tunnel[link]);
+ cave[arrow_location].tunnel[lnk]);
else
(void)printf(
-"*thunk* The arrow can't find a way from %d to %d and flys randomly\n\
+"*thunk* The arrow can't find a way from %d to %d and flies randomly\n\
into room %d!\n",
arrow_location, next,
- cave[arrow_location].tunnel[link]);
- arrow_location = cave[arrow_location].tunnel[link];
+ cave[arrow_location].tunnel[lnk]);
+ arrow_location = cave[arrow_location].tunnel[lnk];
break;
}
chance = random() % 10;
/* each time you shoot, it's more likely the wumpus moves */
static int lastchance = 2;
- if (random() % level == EASY ? 12 : 9 < (lastchance += 2)) {
+ if (random() % (level == EASY ? 12 : 9) < (lastchance += 2)) {
move_wump();
if (wumpus_loc == player_loc)
wump_kill();
return(0);
}
-cave_init()
+static int
+gcd(int a, int b)
+{
+ int r;
+
+ r = a % b;
+ if (r == 0)
+ return (b);
+ return (gcd(b, r));
+}
+
+static void
+cave_init(void)
{
- register int i, j, k, link;
- int delta, int_compare();
- time_t time();
+ int i, j, k, lnk;
+ int delta;
/*
* This does most of the interesting work in this program actually!
for (j = 0; j < link_num ; ++j)
cave[i].tunnel[j] = -1;
- /* choose a random 'hop' delta for our guaranteed link */
- while (!(delta = random() % room_num));
+ /*
+ * Choose a random 'hop' delta for our guaranteed link.
+ * To keep the cave connected, we need the greatest common divisor
+ * of (delta + 1) and room_num to be 1.
+ */
+ do {
+ delta = (random() % (room_num - 1)) + 1;
+ } while (gcd(room_num, delta + 1) != 1);
for (i = 1; i <= room_num; ++i) {
- link = ((i + delta) % room_num) + 1; /* connection */
- cave[i].tunnel[0] = link; /* forw link */
- cave[link].tunnel[1] = i; /* back link */
+ lnk = ((i + delta) % room_num) + 1; /* connection */
+ cave[i].tunnel[0] = lnk; /* forw link */
+ cave[lnk].tunnel[1] = i; /* back link */
}
/* now fill in the rest of the cave with random connections */
for (i = 1; i <= room_num; i++)
for (j = 2; j < link_num ; j++) {
if (cave[i].tunnel[j] != -1)
continue;
-try_again: link = (random() % room_num) + 1;
+try_again: lnk = (random() % room_num) + 1;
/* skip duplicates */
for (k = 0; k < j; k++)
- if (cave[i].tunnel[k] == link)
+ if (cave[i].tunnel[k] == lnk)
goto try_again;
- cave[i].tunnel[j] = link;
+ cave[i].tunnel[j] = lnk;
if (random() % 2 == 1)
continue;
for (k = 0; k < link_num; ++k) {
/* if duplicate, skip it */
- if (cave[link].tunnel[k] == i)
+ if (cave[lnk].tunnel[k] == i)
k = link_num;
/* if open link, use it, force exit */
- if (cave[link].tunnel[k] == -1) {
- cave[link].tunnel[k] = i;
+ if (cave[lnk].tunnel[k] == -1) {
+ cave[lnk].tunnel[k] = i;
k = link_num;
}
}
* make it easier on the intrepid adventurer.
*/
for (i = 1; i <= room_num; ++i)
- qsort(cave[i].tunnel, (u_int)link_num,
+ qsort(cave[i].tunnel, link_num,
sizeof(cave[i].tunnel[0]), int_compare);
#ifdef DEBUG
#endif
}
-clear_things_in_cave()
+static void
+clear_things_in_cave(void)
{
- register int i;
+ int i;
/*
* remove bats and pits from the current cave in preparation for us
cave[i].has_a_bat = cave[i].has_a_pit = 0;
}
-initialize_things_in_cave()
+static void
+initialize_things_in_cave(void)
{
- register int i, loc;
+ int i, loc;
/* place some bats, pits, the wumpus, and the player. */
for (i = 0; i < bat_num; ++i) {
for (i = 0; i < pit_num; ++i) {
do {
loc = (random() % room_num) + 1;
- } while (cave[loc].has_a_pit && cave[loc].has_a_bat);
+ } while (cave[loc].has_a_pit || cave[loc].has_a_bat);
cave[loc].has_a_pit = 1;
#ifdef DEBUG
if (debug)
(void)printf("<wumpus in room %d>\n", loc);
#endif
+ i = 0;
do {
player_loc = (random() % room_num) + 1;
- } while (player_loc == wumpus_loc || (level == HARD ?
- (link_num / room_num < 0.4 ? wump_nearby() : 0) : 0));
+ i++;
+ } while (player_loc == wumpus_loc || cave[player_loc].has_a_pit ||
+ cave[player_loc].has_a_bat || (level == HARD ?
+ (link_num / room_num < 0.4 ? wump_nearby() : 0) : 0) ||
+ (i > 100 && player_loc != wumpus_loc));
}
-getans(prompt)
- char *prompt;
+static int
+getans(const char *prompt)
{
char buf[20];
/* NOTREACHED */
}
-bats_nearby()
-{
- register int i;
+static int
+bats_nearby(void)
+{
+ int i;
/* check for bats in the immediate vicinity */
for (i = 0; i < link_num; ++i)
return(0);
}
-pit_nearby()
-{
- register int i;
+static int
+pit_nearby(void)
+{
+ int i;
/* check for pits in the immediate vicinity */
for (i = 0; i < link_num; ++i)
return(0);
}
-wump_nearby()
+static int
+wump_nearby(void)
{
- register int i, j;
+ int i, j;
/* check for a wumpus within TWO caves of where we are */
for (i = 0; i < link_num; ++i) {
return(0);
}
-move_wump()
+static void
+move_wump(void)
{
wumpus_loc = cave[wumpus_loc].tunnel[random() % link_num];
}
-int_compare(a, b)
- int *a, *b;
+static int
+int_compare(const void *a, const void *b)
{
- return(*a < *b ? -1 : 1);
+ return(*(const int *)a < *(const int *)b ? -1 : 1);
}
-instructions()
+static void
+instructions(void)
{
- char buf[120], *p, *getenv();
+ const char *pager;
+ pid_t pid;
+ int status;
+ int fd;
/*
* read the instructions file, if needed, and show the user how to
return;
}
- if (!(p = getenv("PAGER")) ||
- strlen(p) > sizeof(buf) + strlen(_PATH_WUMPINFO) + 5)
- p = _PATH_PAGER;
-
- (void)sprintf(buf, "%s %s", p, _PATH_WUMPINFO);
- (void)system(buf);
+ if (!isatty(STDOUT_FILENO))
+ pager = "cat";
+ else {
+ if (!(pager = getenv("PAGER")) || (*pager == 0))
+ pager = _PATH_PAGER;
+ }
+ switch (pid = fork()) {
+ case 0: /* child */
+ if ((fd = open(_PATH_WUMPINFO, O_RDONLY)) == -1)
+ err(1, "open %s", _PATH_WUMPINFO);
+ if (dup2(fd, STDIN_FILENO) == -1)
+ err(1, "dup2");
+ (void)execl("/bin/sh", "sh", "-c", pager, (char *) NULL);
+ err(1, "exec sh -c %s", pager);
+ case -1:
+ err(1, "fork");
+ default:
+ (void)waitpid(pid, &status, 0);
+ break;
+ }
}
-usage()
+static void
+usage(void)
{
(void)fprintf(stderr,
"usage: wump [-h] [-a arrows] [-b bats] [-p pits] [-r rooms] [-t tunnels]\n");
/* messages */
-wump_kill()
+static void
+wump_kill(void)
{
(void)printf(
"*ROAR* *chomp* *snurfle* *chomp*!\n\
passed out from the stench!\n");
}
-kill_wump()
+static void
+kill_wump(void)
{
(void)printf(
"*thwock!* *groan* *crash*\n\n\
mightiest adventurer at a single whiff!!\n");
}
-no_arrows()
+static void
+no_arrows(void)
{
(void)printf(
"\nYou turn and look at your quiver, and realize with a sinking feeling\n\
that you've just shot your last arrow (figuratively, too). Sensing this\n\
-with its psychic powers, the evil Wumpus rampagees through the cave, finds\n\
+with its psychic powers, the evil Wumpus rampages through the cave, finds\n\
you, and with a mighty *ROAR* eats you alive!\n");
}
-shoot_self()
+static void
+shoot_self(void)
{
(void)printf(
"\n*Thwack!* A sudden piercing feeling informs you that the ricochet\n\
(*CHOMP*)\n");
}
-jump(where)
- int where;
+static void
+jump(int where)
{
(void)printf(
"\nWith a jaunty step you enter the magic tunnel. As you do, you\n\
a very curious, warm sensation and find yourself in room %d!!\n", where);
}
-pit_kill()
+static void
+pit_kill(void)
{
(void)printf(
"*AAAUUUUGGGGGHHHHHhhhhhhhhhh...*\n\
you can at least find out if Jules Verne was right...\n");
}
-pit_survive()
+static void
+pit_survive(void)
{
(void)printf(
"Without conscious thought you grab for the side of the cave and manage\n\