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