(linenum→info "unix/slp.c:2238")

bsd-games/2.17/wump/wump.c

    1: /*      $NetBSD: wump.c,v 1.17 2005/02/15 12:56:20 jsm Exp $ */
    2: 
    3: /*
    4:  * Copyright (c) 1989, 1993
    5:  *      The Regents of the University of California.  All rights reserved.
    6:  * All rights reserved.
    7:  *
    8:  * This code is derived from software contributed to Berkeley by
    9:  * Dave Taylor, of Intuitive Systems.
   10:  *
   11:  * Redistribution and use in source and binary forms, with or without
   12:  * modification, are permitted provided that the following conditions
   13:  * are met:
   14:  * 1. Redistributions of source code must retain the above copyright
   15:  *    notice, this list of conditions and the following disclaimer.
   16:  * 2. Redistributions in binary form must reproduce the above copyright
   17:  *    notice, this list of conditions and the following disclaimer in the
   18:  *    documentation and/or other materials provided with the distribution.
   19:  * 3. Neither the name of the University nor the names of its contributors
   20:  *    may be used to endorse or promote products derived from this software
   21:  *    without specific prior written permission.
   22:  *
   23:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   24:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   25:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   26:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   27:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   28:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   29:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   30:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   31:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   32:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   33:  * SUCH DAMAGE.
   34:  */
   35: 
   36: #include <sys/cdefs.h>
   37: #ifndef lint
   38: __COPYRIGHT("@(#) Copyright (c) 1989, 1993\n\
   39:         The Regents of the University of California.  All rights reserved.\n");
   40: #endif /* not lint */
   41: 
   42: #ifndef lint
   43: #if 0
   44: static char sccsid[] = "@(#)wump.c      8.1 (Berkeley) 5/31/93";
   45: #else
   46: __RCSID("$NetBSD: wump.c,v 1.17 2005/02/15 12:56:20 jsm Exp $");
   47: #endif
   48: #endif /* not lint */
   49: 
   50: /*
   51:  * A very new version of the age old favorite Hunt-The-Wumpus game that has
   52:  * been a part of the BSD distribution of Unix for longer than us old folk
   53:  * would care to remember.
   54:  */
   55: 
   56: #include <err.h>
   57: #include <sys/types.h>
   58: #include <sys/file.h>
   59: #include <sys/wait.h>
   60: #include <stdio.h>
   61: #include <stdlib.h>
   62: #include <string.h>
   63: #include <time.h>
   64: #include <unistd.h>
   65: #include "pathnames.h"
   66: 
   67: /* some defines to spec out what our wumpus cave should look like */
   68: 
   69: #define MAX_ARROW_SHOT_DISTANCE 6               /* +1 for '0' stopper */
   70: #define MAX_LINKS_IN_ROOM       25            /* a complex cave */
   71: 
   72: #define MAX_ROOMS_IN_CAVE       250
   73: #define ROOMS_IN_CAVE           20
   74: #define MIN_ROOMS_IN_CAVE       10
   75: 
   76: #define LINKS_IN_ROOM           3
   77: #define NUMBER_OF_ARROWS        5
   78: #define PIT_COUNT               3
   79: #define BAT_COUNT               3
   80: 
   81: #define EASY                    1                /* levels of play */
   82: #define HARD                    2
   83: 
   84: /* some macro definitions for cleaner output */
   85: 
   86: #define plural(n)       (n == 1 ? "" : "s")
   87: 
   88: /* simple cave data structure; +1 so we can index from '1' not '0' */
   89: struct room_record {
   90:         int tunnel[MAX_LINKS_IN_ROOM];
   91:         int has_a_pit, has_a_bat;
   92: } cave[MAX_ROOMS_IN_CAVE+1];
   93: 
   94: /*
   95:  * global variables so we can keep track of where the player is, how
   96:  * many arrows they still have, where el wumpo is, and so on...
   97:  */
   98: int player_loc = -1;                    /* player location */
   99: int wumpus_loc = -1;                    /* The Bad Guy location */
  100: int level = EASY;                       /* level of play */
  101: int arrows_left;                        /* arrows unshot */
  102: 
  103: #ifdef DEBUG
  104: int debug = 0;
  105: #endif
  106: 
  107: int pit_num = PIT_COUNT;                /* # pits in cave */
  108: int bat_num = BAT_COUNT;                /* # bats */
  109: int room_num = ROOMS_IN_CAVE;           /* # rooms in cave */
  110: int link_num = LINKS_IN_ROOM;           /* links per room  */
  111: int arrow_num = NUMBER_OF_ARROWS;       /* arrow inventory */
  112: 
  113: char answer[20];                        /* user input */
  114: 
  115: int     bats_nearby(void);
  116: void    cave_init(void);
  117: void    clear_things_in_cave(void);
  118: void    display_room_stats(void);
  119: int     gcd(int, int);
  120: int     getans(const char *);
  121: void    initialize_things_in_cave(void);
  122: void    instructions(void);
  123: int     int_compare(const void *, const void *);
  124: void    jump(int);
  125: void    kill_wump(void);
  126: int     main(int, char **);
  127: int     move_to(const char *);
  128: void    move_wump(void);
  129: void    no_arrows(void);
  130: void    pit_kill(void);
  131: int     pit_nearby(void);
  132: void    pit_survive(void);
  133: int     shoot(char *);
  134: void    shoot_self(void);
  135: int     take_action(void);
  136: void    usage(void) __attribute__((__noreturn__));
  137: void    wump_kill(void);
  138: int     wump_nearby(void);
  139: 
  140: int
  141: main(argc, argv)
  142:         int argc;
  143:         char **argv;
  144: {
  145:         int c;
  146: 
  147:         /* Revoke setgid privileges */
  148:         setregid(getgid(), getgid());
  149: 
  150: #ifdef DEBUG
  151:         while ((c = getopt(argc, argv, "a:b:hp:r:t:d")) != -1)
  152: #else
  153:         while ((c = getopt(argc, argv, "a:b:hp:r:t:")) != -1)
  154: #endif
  155:                 switch (c) {
  156:                 case 'a':
  157:                         arrow_num = atoi(optarg);
  158:                         break;
  159:                 case 'b':
  160:                         bat_num = atoi(optarg);
  161:                         break;
  162: #ifdef DEBUG
  163:                 case 'd':
  164:                         debug = 1;
  165:                         break;
  166: #endif
  167:                 case 'h':
  168:                         level = HARD;
  169:                         break;
  170:                 case 'p':
  171:                         pit_num = atoi(optarg);
  172:                         break;
  173:                 case 'r':
  174:                         room_num = atoi(optarg);
  175:                         if (room_num < MIN_ROOMS_IN_CAVE) {
  176:                                 (void)fprintf(stderr,
  177:         "No self-respecting wumpus would live in such a small cave!\n");
  178:                                 exit(1);
  179:                         }
  180:                         if (room_num > MAX_ROOMS_IN_CAVE) {
  181:                                 (void)fprintf(stderr,
  182:         "Even wumpii can't furnish caves that large!\n");
  183:                                 exit(1);
  184:                         }
  185:                         break;
  186:                 case 't':
  187:                         link_num = atoi(optarg);
  188:                         if (link_num < 2) {
  189:                                 (void)fprintf(stderr,
  190:         "Wumpii like extra doors in their caves!\n");
  191:                                 exit(1);
  192:                         }
  193:                         break;
  194:                 case '?':
  195:                 default:
  196:                         usage();
  197:         }
  198: 
  199:         if (link_num > MAX_LINKS_IN_ROOM ||
  200:             link_num > room_num - (room_num / 4)) {
  201:                 (void)fprintf(stderr,
  202: "Too many tunnels!  The cave collapsed!\n(Fortunately, the wumpus escaped!)\n");
  203:                 exit(1);
  204:         }
  205: 
  206:         if (level == HARD) {
  207:                 bat_num += ((random() % (room_num / 2)) + 1);
  208:                 pit_num += ((random() % (room_num / 2)) + 1);
  209:         }
  210: 
  211:         if (bat_num > room_num / 2) {
  212:                 (void)fprintf(stderr,
  213: "The wumpus refused to enter the cave, claiming it was too crowded!\n");
  214:                 exit(1);
  215:         }
  216: 
  217:         if (pit_num > room_num / 2) {
  218:                 (void)fprintf(stderr,
  219: "The wumpus refused to enter the cave, claiming it was too dangerous!\n");
  220:                 exit(1);
  221:         }
  222: 
  223:         instructions();
  224:         cave_init();
  225: 
  226:         /* and we're OFF!  da dum, da dum, da dum, da dum... */
  227:         (void)printf(
  228: "\nYou're in a cave with %d rooms and %d tunnels leading from each room.\n\
  229: There are %d bat%s and %d pit%s scattered throughout the cave, and your\n\
  230: quiver holds %d custom super anti-evil Wumpus arrows.  Good luck.\n",
  231:             room_num, link_num, bat_num, plural(bat_num), pit_num,
  232:             plural(pit_num), arrow_num);
  233: 
  234:         for (;;) {
  235:                 initialize_things_in_cave();
  236:                 arrows_left = arrow_num;
  237:                 do {
  238:                         display_room_stats();
  239:                         (void)printf("Move or shoot? (m-s) ");
  240:                         (void)fflush(stdout);
  241:                         if (!fgets(answer, sizeof(answer), stdin))
  242:                                 break;
  243:                 } while (!take_action());
  244: 
  245:                 if (!getans("\nCare to play another game? (y-n) "))
  246:                         exit(0);
  247:                 if (getans("In the same cave? (y-n) "))
  248:                         clear_things_in_cave();
  249:                 else
  250:                         cave_init();
  251:         }
  252:         /* NOTREACHED */
  253:         return (0);
  254: }
  255: 
  256: void
  257: display_room_stats()
  258: {
  259:         int i;
  260: 
  261:         /*
  262:          * Routine will explain what's going on with the current room, as well
  263:          * as describe whether there are pits, bats, & wumpii nearby.  It's
  264:          * all pretty mindless, really.
  265:          */
  266:         (void)printf(
  267: "\nYou are in room %d of the cave, and have %d arrow%s left.\n",
  268:             player_loc, arrows_left, plural(arrows_left));
  269: 
  270:         if (bats_nearby())
  271:                 (void)printf("*rustle* *rustle* (must be bats nearby)\n");
  272:         if (pit_nearby())
  273:                 (void)printf("*whoosh* (I feel a draft from some pits).\n");
  274:         if (wump_nearby())
  275:                 (void)printf("*sniff* (I can smell the evil Wumpus nearby!)\n");
  276: 
  277:         (void)printf("There are tunnels to rooms %d, ",
  278:            cave[player_loc].tunnel[0]);
  279: 
  280:         for (i = 1; i < link_num - 1; i++)
  281:                 if (cave[player_loc].tunnel[i] <= room_num)
  282:                         (void)printf("%d, ", cave[player_loc].tunnel[i]);
  283:         (void)printf("and %d.\n", cave[player_loc].tunnel[link_num - 1]);
  284: }
  285: 
  286: int
  287: take_action()
  288: {
  289:         /*
  290:          * Do the action specified by the player, either 'm'ove, 's'hoot
  291:          * or something exceptionally bizarre and strange!  Returns 1
  292:          * iff the player died during this turn, otherwise returns 0.
  293:          */
  294:         switch (*answer) {
  295:                 case 'M':
  296:                 case 'm':                     /* move */
  297:                         return(move_to(answer + 1));
  298:                 case 'S':
  299:                 case 's':                     /* shoot */
  300:                         return(shoot(answer + 1));
  301:                 case 'Q':
  302:                 case 'q':
  303:                 case 'x':
  304:                         exit(0);
  305:                 case '\n':
  306:                         return(0);
  307:                 }
  308:         if (random() % 15 == 1)
  309:                 (void)printf("Que pasa?\n");
  310:         else
  311:                 (void)printf("I don't understand!\n");
  312:         return(0);
  313: }
  314: 
  315: int
  316: move_to(room_number)
  317:         const char *room_number;
  318: {
  319:         int i, just_moved_by_bats, next_room, tunnel_available;
  320: 
  321:         /*
  322:          * This is responsible for moving the player into another room in the
  323:          * cave as per their directions.  If room_number is a null string,
  324:          * then we'll prompt the user for the next room to go into.   Once
  325:          * we've moved into the room, we'll check for things like bats, pits,
  326:          * and so on.  This routine returns 1 if something occurs that kills
  327:          * the player and 0 otherwise...
  328:          */
  329:         tunnel_available = just_moved_by_bats = 0;
  330:         next_room = atoi(room_number);
  331: 
  332:         /* crap for magic tunnels */
  333:         if (next_room == room_num + 1 &&
  334:             cave[player_loc].tunnel[link_num-1] != next_room)
  335:                 ++next_room;
  336: 
  337:         while (next_room < 1 || next_room > room_num + 1) {
  338:                 if (next_room < 0 && next_room != -1)
  339: (void)printf("Sorry, but we're constrained to a semi-Euclidean cave!\n");
  340:                 if (next_room > room_num + 1)
  341: (void)printf("What?  The cave surely isn't quite that big!\n");
  342:                 if (next_room == room_num + 1 &&
  343:                     cave[player_loc].tunnel[link_num-1] != next_room) {
  344:                         (void)printf("What?  The cave isn't that big!\n");
  345:                         ++next_room;
  346:                 }
  347:                 (void)printf("To which room do you wish to move? ");
  348:                 (void)fflush(stdout);
  349:                 if (!fgets(answer, sizeof(answer), stdin))
  350:                         return(1);
  351:                 next_room = atoi(answer);
  352:         }
  353: 
  354:         /* now let's see if we can move to that room or not */
  355:         tunnel_available = 0;
  356:         for (i = 0; i < link_num; i++)
  357:                 if (cave[player_loc].tunnel[i] == next_room)
  358:                         tunnel_available = 1;
  359: 
  360:         if (!tunnel_available) {
  361:                 (void)printf("*Oof!*  (You hit the wall)\n");
  362:                 if (random() % 6 == 1) {
  363: (void)printf("Your colorful comments awaken the wumpus!\n");
  364:                         move_wump();
  365:                         if (wumpus_loc == player_loc) {
  366:                                 wump_kill();
  367:                                 return(1);
  368:                         }
  369:                 }
  370:                 return(0);
  371:         }
  372: 
  373:         /* now let's move into that room and check it out for dangers */
  374:         if (next_room == room_num + 1)
  375:                 jump(next_room = (random() % room_num) + 1);
  376: 
  377:         player_loc = next_room;
  378:         for (;;) {
  379:                 if (next_room == wumpus_loc) {                /* uh oh... */
  380:                         wump_kill();
  381:                         return(1);
  382:                 }
  383:                 if (cave[next_room].has_a_pit) {
  384:                         if (random() % 12 < 2) {
  385:                                 pit_survive();
  386:                                 return(0);
  387:                         } else {
  388:                                 pit_kill();
  389:                                 return(1);
  390:                         }
  391:                 }
  392: 
  393:                 if (cave[next_room].has_a_bat) {
  394:                         (void)printf(
  395: "*flap*  *flap*  *flap*  (humongous bats pick you up and move you%s!)\n",
  396:                             just_moved_by_bats ? " again": "");
  397:                         next_room = player_loc = (random() % room_num) + 1;
  398:                         just_moved_by_bats = 1;
  399:                 }
  400: 
  401:                 else
  402:                         break;
  403:         }
  404:         return(0);
  405: }
  406: 
  407: int
  408: shoot(room_list)
  409:         char *room_list;
  410: {
  411:         int chance, next, roomcnt;
  412:         int j, arrow_location, link, ok;
  413:         char *p;
  414: 
  415:         /*
  416:          * Implement shooting arrows.  Arrows are shot by the player indicating
  417:          * a space-separated list of rooms that the arrow should pass through;
  418:          * if any of the rooms they specify are not accessible via tunnel from
  419:          * the room the arrow is in, it will instead fly randomly into another
  420:          * room.  If the player hits the wumpus, this routine will indicate
  421:          * such.  If it misses, this routine will *move* the wumpus one room.
  422:          * If it's the last arrow, the player then dies...  Returns 1 if the
  423:          * player has won or died, 0 if nothing has happened.
  424:          */
  425:         arrow_location = player_loc;
  426:         for (roomcnt = 1;; ++roomcnt, room_list = NULL) {
  427:                 if (!(p = strtok(room_list, " \t\n"))) {
  428:                         if (roomcnt == 1) {
  429:                                 (void)printf(
  430:                         "The arrow falls to the ground at your feet!\n");
  431:                                 return(0);
  432:                         } else
  433:                                 break;
  434:                 }
  435:                 if (roomcnt > 5) {
  436:                         (void)printf(
  437: "The arrow wavers in its flight and and can go no further!\n");
  438:                         break;
  439:                 }
  440:                 next = atoi(p);
  441:                 for (j = 0, ok = 0; j < link_num; j++)
  442:                         if (cave[arrow_location].tunnel[j] == next)
  443:                                 ok = 1;
  444: 
  445:                 if (ok) {
  446:                         if (next > room_num) {
  447:                                 (void)printf(
  448: "A faint gleam tells you the arrow has gone through a magic tunnel!\n");
  449:                                 arrow_location = (random() % room_num) + 1;
  450:                         } else
  451:                                 arrow_location = next;
  452:                 } else {
  453:                         link = (random() % link_num);
  454:                         if (link == player_loc)
  455:                                 (void)printf(
  456: "*thunk*  The arrow can't find a way from %d to %d and flys back into\n\
  457: your room!\n",
  458:                                     arrow_location, next);
  459:                         else if (cave[arrow_location].tunnel[link] > room_num)
  460:                                 (void)printf(
  461: "*thunk*  The arrow flys randomly into a magic tunnel, thence into\n\
  462: room %d!\n",
  463:                                     cave[arrow_location].tunnel[link]);
  464:                         else
  465:                                 (void)printf(
  466: "*thunk*  The arrow can't find a way from %d to %d and flys randomly\n\
  467: into room %d!\n",
  468:                                     arrow_location, next,
  469:                                     cave[arrow_location].tunnel[link]);
  470:                         arrow_location = cave[arrow_location].tunnel[link];
  471:                         break;
  472:                 }
  473:                 chance = random() % 10;
  474:                 if (roomcnt == 3 && chance < 2) {
  475:                         (void)printf(
  476: "Your bowstring breaks!  *twaaaaaang*\n\
  477: The arrow is weakly shot and can go no further!\n");
  478:                         break;
  479:                 } else if (roomcnt == 4 && chance < 6) {
  480:                         (void)printf(
  481: "The arrow wavers in its flight and and can go no further!\n");
  482:                         break;
  483:                 }
  484:         }
  485: 
  486:         /*
  487:          * now we've gotten into the new room let us see if El Wumpo is
  488:          * in the same room ... if so we've a HIT and the player WON!
  489:          */
  490:         if (arrow_location == wumpus_loc) {
  491:                 kill_wump();
  492:                 return(1);
  493:         }
  494: 
  495:         if (arrow_location == player_loc) {
  496:                 shoot_self();
  497:                 return(1);
  498:         }
  499: 
  500:         if (!--arrows_left) {
  501:                 no_arrows();
  502:                 return(1);
  503:         }
  504: 
  505:         {
  506:                 /* each time you shoot, it's more likely the wumpus moves */
  507:                 static int lastchance = 2;
  508: 
  509:                 if (random() % level == EASY ? 12 : 9 < (lastchance += 2)) {
  510:                         move_wump();
  511:                         if (wumpus_loc == player_loc)
  512:                                 wump_kill();
  513:                         lastchance = random() % 3;
  514: 
  515:                 }
  516:         }
  517:         return(0);
  518: }
  519: 
  520: int
  521: gcd(a, b)
  522:         int a, b;
  523: {
  524:         int r;
  525: 
  526:         r = a % b;
  527:         if (r == 0)
  528:                 return (b);
  529:         return (gcd(b, r));
  530: }
  531: 
  532: void
  533: cave_init()
  534: {
  535:         int i, j, k, link;
  536:         int delta;
  537: 
  538:         /*
  539:          * This does most of the interesting work in this program actually!
  540:          * In this routine we'll initialize the Wumpus cave to have all rooms
  541:          * linking to all others by stepping through our data structure once,
  542:          * recording all forward links and backwards links too.  The parallel
  543:          * "linkcount" data structure ensures that no room ends up with more
  544:          * than three links, regardless of the quality of the random number
  545:          * generator that we're using.
  546:          */
  547:         srandom((int)time((time_t *)0));
  548: 
  549:         /* initialize the cave first off. */
  550:         for (i = 1; i <= room_num; ++i)
  551:                 for (j = 0; j < link_num ; ++j)
  552:                         cave[i].tunnel[j] = -1;
  553: 
  554:         /*
  555:          * Choose a random 'hop' delta for our guaranteed link.
  556:          * To keep the cave connected, we need the greatest common divisor
  557:          * of (delta + 1) and room_num to be 1.
  558:          */
  559:         do {
  560:                 delta = (random() % (room_num - 1)) + 1;
  561:         } while (gcd(room_num, delta + 1) != 1);
  562: 
  563:         for (i = 1; i <= room_num; ++i) {
  564:                 link = ((i + delta) % room_num) + 1;  /* connection */
  565:                 cave[i].tunnel[0] = link;             /* forw link */
  566:                 cave[link].tunnel[1] = i;             /* back link */
  567:         }
  568:         /* now fill in the rest of the cave with random connections */
  569:         for (i = 1; i <= room_num; i++)
  570:                 for (j = 2; j < link_num ; j++) {
  571:                         if (cave[i].tunnel[j] != -1)
  572:                                 continue;
  573: try_again: