1 /* $OpenBSD: wump.c,v 1.26 2013/08/29 20:22:22 naddy 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 if (level == HARD) { 205 if (room_num / 2 - bat_num) 206 bat_num += arc4random_uniform(room_num / 2 - bat_num); 207 if (room_num / 2 - pit_num) 208 pit_num += arc4random_uniform(room_num / 2 - pit_num); 209 } 210 211 /* Leave at least two rooms free--one for the player to start in, and 212 * potentially one for the wumpus. 213 */ 214 if (bat_num > room_num / 2 - 1) 215 errx(1, 216 "the wumpus refused to enter the cave, claiming it was too crowded!"); 217 218 if (pit_num > room_num / 2 - 1) 219 errx(1, 220 "the wumpus refused to enter the cave, claiming it was too dangerous!"); 221 222 instructions(); 223 if (oldstyle) 224 dodecahedral_cave_init(); 225 else 226 cave_init(); 227 228 /* and we're OFF! da dum, da dum, da dum, da dum... */ 229 (void)printf( 230 "\nYou're in a cave with %d rooms and %d tunnels leading from each room.\n\ 231 There are %d bat%s and %d pit%s scattered throughout the cave, and your\n\ 232 quiver holds %d custom super anti-evil Wumpus arrows. Good luck.\n", 233 room_num, link_num, bat_num, plural(bat_num), pit_num, 234 plural(pit_num), arrow_num); 235 236 for (;;) { 237 initialize_things_in_cave(); 238 arrows_left = arrow_num; 239 do { 240 display_room_stats(); 241 (void)printf("Move or shoot? (m-s) "); 242 (void)fflush(stdout); 243 (void)fpurge(stdin); 244 if (!fgets(answer, sizeof(answer), stdin)) 245 break; 246 } while (!take_action()); 247 (void)fpurge(stdin); 248 249 if (!getans("\nCare to play another game? (y-n) ")) { 250 (void)printf("\n"); 251 exit(0); 252 } 253 clear_things_in_cave(); 254 if (!getans("In the same cave? (y-n) ")) { 255 if (oldstyle) 256 dodecahedral_cave_init(); 257 else 258 cave_init(); 259 } 260 } 261 /* NOTREACHED */ 262 } 263 264 void 265 display_room_stats(void) 266 { 267 int i; 268 269 /* 270 * Routine will explain what's going on with the current room, as well 271 * as describe whether there are pits, bats, & wumpii nearby. It's 272 * all pretty mindless, really. 273 */ 274 (void)printf( 275 "\nYou are in room %d of the cave, and have %d arrow%s left.\n", 276 player_loc, arrows_left, plural(arrows_left)); 277 278 if (bats_nearby()) 279 (void)printf("*rustle* *rustle* (must be bats nearby)\n"); 280 if (pit_nearby()) 281 (void)printf("*whoosh* (I feel a draft from some pits).\n"); 282 if (wump_nearby()) 283 (void)printf("*sniff* (I can smell the evil Wumpus nearby!)\n"); 284 285 (void)printf("There are tunnels to rooms %d, ", 286 cave[player_loc].tunnel[0]); 287 288 for (i = 1; i < link_num - 1; i++) 289 /* if (cave[player_loc].tunnel[i] <= room_num) */ 290 (void)printf("%d, ", cave[player_loc].tunnel[i]); 291 (void)printf("and %d.\n", cave[player_loc].tunnel[link_num - 1]); 292 } 293 294 int 295 take_action(void) 296 { 297 /* 298 * Do the action specified by the player, either 'm'ove, 's'hoot 299 * or something exceptionally bizarre and strange! Returns 1 300 * iff the player died during this turn, otherwise returns 0. 301 */ 302 switch (*answer) { 303 case 'M': 304 case 'm': /* move */ 305 return(move_to(answer + 1)); 306 case 'S': 307 case 's': /* shoot */ 308 return(shoot(answer + 1)); 309 case 'Q': 310 case 'q': 311 case 'x': 312 exit(0); 313 case '\n': 314 return(0); 315 } 316 if (arc4random_uniform(15) == 1) 317 (void)printf("Que pasa?\n"); 318 else 319 (void)printf("I don't understand!\n"); 320 return(0); 321 } 322 323 int 324 move_to(const char *room_number) 325 { 326 int i, just_moved_by_bats, next_room, tunnel_available; 327 328 /* 329 * This is responsible for moving the player into another room in the 330 * cave as per their directions. If room_number is a null string, 331 * then we'll prompt the user for the next room to go into. Once 332 * we've moved into the room, we'll check for things like bats, pits, 333 * and so on. This routine returns 1 if something occurs that kills 334 * the player and 0 otherwise... 335 */ 336 tunnel_available = just_moved_by_bats = 0; 337 next_room = atoi(room_number); 338 339 /* crap for magic tunnels */ 340 /* if (next_room == room_num + 1 && 341 * cave[player_loc].tunnel[link_num-1] != next_room) 342 * ++next_room; 343 */ 344 while (next_room < 1 || next_room > room_num /* + 1 */) { 345 if (next_room < 0 && next_room != -1) 346 (void)printf("Sorry, but we're constrained to a semi-Euclidean cave!\n"); 347 if (next_room > room_num /* + 1 */) 348 (void)printf("What? The cave surely isn't quite that big!\n"); 349 /* if (next_room == room_num + 1 && 350 * cave[player_loc].tunnel[link_num-1] != next_room) { 351 * (void)printf("What? The cave isn't that big!\n"); 352 * ++next_room; 353 * } 354 */ (void)printf("To which room do you wish to move? "); 355 (void)fflush(stdout); 356 if (!fgets(answer, sizeof(answer), stdin)) 357 return(1); 358 next_room = atoi(answer); 359 } 360 361 /* now let's see if we can move to that room or not */ 362 tunnel_available = 0; 363 for (i = 0; i < link_num; i++) 364 if (cave[player_loc].tunnel[i] == next_room) 365 tunnel_available = 1; 366 367 if (!tunnel_available) { 368 (void)printf("*Oof!* (You hit the wall)\n"); 369 if (arc4random_uniform(6) == 1) { 370 (void)printf("Your colorful comments awaken the wumpus!\n"); 371 move_wump(); 372 if (wumpus_loc == player_loc) { 373 wump_walk_kill(); 374 return(1); 375 } 376 } 377 return(0); 378 } 379 380 /* now let's move into that room and check it out for dangers */ 381 /* if (next_room == room_num + 1) 382 * jump(next_room = arc4random_uniform(room_num) + 1); 383 */ 384 player_loc = next_room; 385 for (;;) { 386 if (next_room == wumpus_loc) { /* uh oh... */ 387 if (just_moved_by_bats) 388 wump_bat_kill(); 389 else 390 wump_kill(); 391 return(1); 392 } 393 if (cave[next_room].has_a_pit) { 394 if (arc4random_uniform(12) < 2) { 395 pit_survive(); 396 return(0); 397 } else { 398 if (just_moved_by_bats) 399 pit_kill_bat(); 400 else 401 pit_kill(); 402 return(1); 403 } 404 } 405 406 if (cave[next_room].has_a_bat) { 407 (void)printf( 408 "*flap* *flap* *flap* (humongous bats pick you up and move you%s!)\n", 409 just_moved_by_bats ? " again": ""); 410 next_room = player_loc = 411 arc4random_uniform(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 = arc4random_uniform(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 = 486 * arc4random_uniform(room_num) + 1; 487 * } else 488 */ arrow_location = next; 489 } else { 490 link = (arc4random_uniform(link_num)); 491 if (cave[arrow_location].tunnel[link] == player_loc) 492 (void)printf( 493 "*thunk* The arrow can't find a way from %d to %d and flies back into\n\ 494 your room!\n", 495 arrow_location, next); 496 /* else if (cave[arrow_location].tunnel[link] > room_num) 497 * (void)printf( 498 *"*thunk* The arrow flies randomly into a magic tunnel, thence into\n\ 499 *room %d!\n", 500 * cave[arrow_location].tunnel[link]); 501 */ else 502 (void)printf( 503 "*thunk* The arrow can't find a way from %d to %d and flies randomly\n\ 504 into room %d!\n", arrow_location, next, cave[arrow_location].tunnel[link]); 505 506 arrow_location = cave[arrow_location].tunnel[link]; 507 } 508 509 /* 510 * now we've gotten into the new room let us see if El Wumpo is 511 * in the same room ... if so we've a HIT and the player WON! 512 */ 513 if (arrow_location == wumpus_loc) { 514 kill_wump(); 515 return(1); 516 } 517 518 if (arrow_location == player_loc) { 519 shoot_self(); 520 return(1); 521 } 522 } 523 524 if (!--arrows_left) { 525 no_arrows(); 526 return(1); 527 } 528 529 { 530 /* each time you shoot, it's more likely the wumpus moves */ 531 static int lastchance = 2; 532 533 if (arc4random_uniform(level) == EASY ? 534 12 : 9 < (lastchance += 2)) { 535 move_wump(); 536 if (wumpus_loc == player_loc) { 537 wump_walk_kill(); 538 /* Reset for next game */ 539 lastchance = arc4random_uniform(3); 540 return(1); 541 } 542 543 } 544 } 545 (void)printf("The arrow hit nothing.\n"); 546 return(0); 547 } 548 549 int 550 gcd(int a, int b) 551 { 552 int r; 553 554 if (!(r = (a % b))) 555 return(b); 556 return(gcd(b, r)); 557 } 558 559 void 560 cave_init(void) 561 { 562 int i, j, k, link; 563 int delta; 564 565 /* 566 * This does most of the interesting work in this program actually! 567 * In this routine we'll initialize the Wumpus cave to have all rooms 568 * linking to all others by stepping through our data structure once, 569 * recording all forward links and backwards links too. The parallel 570 * "linkcount" data structure ensures that no room ends up with more 571 * than three links, regardless of the quality of the random number 572 * generator that we're using. 573 */ 574 575 /* Note that throughout the source there are commented-out vestigial 576 * remains of the 'magic tunnel', which was a tunnel to room 577 * room_num +1. It was necessary if all paths were two-way and 578 * there was an odd number of rooms, each with an odd number of 579 * exits. It's being kept in case cave_init ever gets reworked into 580 * something more traditional. 581 */ 582 583 /* initialize the cave first off. */ 584 for (i = 1; i <= room_num; ++i) 585 for (j = 0; j < link_num ; ++j) 586 cave[i].tunnel[j] = -1; 587 588 /* choose a random 'hop' delta for our guaranteed link. 589 * To keep the cave connected, require greatest common 590 * divisor of (delta + 1) and room_num to be 1 591 */ 592 do { 593 delta = arc4random_uniform(room_num - 1) + 1; 594 } while (gcd(room_num, delta + 1) != 1); 595 596 for (i = 1; i <= room_num; ++i) { 597 link = ((i + delta) % room_num) + 1; /* connection */ 598 cave[i].tunnel[0] = link; /* forw link */ 599 cave[link].tunnel[1] = i; /* back link */ 600 } 601 /* now fill in the rest of the cave with random connections. 602 * This is a departure from historical versions of wumpus. 603 */ 604 for (i = 1; i <= room_num; i++) 605 for (j = 2; j < link_num ; j++) { 606 if (cave[i].tunnel[j] != -1) 607 continue; 608 try_again: link = arc4random_uniform(room_num) + 1; 609 /* skip duplicates */ 610 for (k = 0; k < j; k++) 611 if (cave[i].tunnel[k] == link) 612 goto try_again; 613 /* don't let a room connect to itself */ 614 if (link == i) 615 goto try_again; 616 cave[i].tunnel[j] = link; 617 if (arc4random() % 2 == 1) 618 continue; 619 for (k = 0; k < link_num; ++k) { 620 /* if duplicate, skip it */ 621 if (cave[link].tunnel[k] == i) 622 k = link_num; 623 else { 624 /* if open link, use it, force exit */ 625 if (cave[link].tunnel[k] == -1) { 626 cave[link].tunnel[k] = i; 627 k = link_num; 628 } 629 } 630 } 631 } 632 /* 633 * now that we're done, sort the tunnels in each of the rooms to 634 * make it easier on the intrepid adventurer. 635 */ 636 for (i = 1; i <= room_num; ++i) 637 qsort(cave[i].tunnel, (u_int)link_num, 638 sizeof(cave[i].tunnel[0]), int_compare); 639 640 #ifdef DEBUG 641 if (debug) 642 for (i = 1; i <= room_num; ++i) { 643 (void)printf("<room %d has tunnels to ", i); 644 for (j = 0; j < link_num; ++j) 645 (void)printf("%d ", cave[i].tunnel[j]); 646 (void)printf(">\n"); 647 } 648 #endif 649 } 650 651 void 652 dodecahedral_cave_init(void) 653 { 654 int vert[20][3] = { 655 {1, 4, 7}, 656 {0, 2, 9}, 657 {1, 3, 11}, 658 {2, 4, 13}, 659 {0, 3, 5}, 660 {4, 6, 14}, 661 {5, 7, 16}, 662 {0, 6, 8}, 663 {7, 9, 17}, 664 {1, 8, 10}, 665 {9, 11, 18}, 666 {2, 10, 12}, 667 {11, 13, 19}, 668 {3, 12, 14}, 669 {5, 13, 15}, 670 {14, 16, 19}, 671 {6, 15, 17}, 672 {8, 16, 18}, 673 {10, 17, 19}, 674 {12, 15, 18}, 675 }; 676 int loc[20]; 677 int i, j, temp; 678 679 if (room_num != 20 || link_num != 3) 680 errx(1, "wrong parameters for dodecahedron"); 681 for (i = 0; i < 20; i++) 682 loc[i] = i; 683 for (i = 0; i < 20; i++) { 684 j = arc4random_uniform(20 - i); 685 if (j) { 686 temp = loc[i]; 687 loc[i] = loc[i + j]; 688 loc[i + j] = temp; 689 } 690 } 691 /* cave is offset by 1 */ 692 for (i = 0; i < 20; i++) { 693 for (j = 0; j < 3; j++) 694 cave[loc[i] + 1].tunnel[j] = loc[vert[i][j]] + 1; 695 } 696 697 /* 698 * now that we're done, sort the tunnels in each of the rooms to 699 * make it easier on the intrepid adventurer. 700 */ 701 for (i = 1; i <= room_num; ++i) 702 qsort(cave[i].tunnel, (u_int)link_num, 703 sizeof(cave[i].tunnel[0]), int_compare); 704 705 #ifdef DEBUG 706 if (debug) 707 for (i = 1; i <= room_num; ++i) { 708 (void)printf("<room %d has tunnels to ", i); 709 for (j = 0; j < link_num; ++j) 710 (void)printf("%d ", cave[i].tunnel[j]); 711 (void)printf(">\n"); 712 } 713 #endif 714 } 715 716 void 717 clear_things_in_cave(void) 718 { 719 int i; 720 721 /* 722 * remove bats and pits from the current cave in preparation for us 723 * adding new ones via the initialize_things_in_cave() routines. 724 */ 725 for (i = 1; i <= room_num; ++i) 726 cave[i].has_a_bat = cave[i].has_a_pit = 0; 727 } 728 729 void 730 initialize_things_in_cave(void) 731 { 732 int i, loc; 733 734 /* place some bats, pits, the wumpus, and the player. */ 735 for (i = 0; i < bat_num; ++i) { 736 do { 737 loc = arc4random_uniform(room_num) + 1; 738 } while (cave[loc].has_a_bat); 739 cave[loc].has_a_bat = 1; 740 #ifdef DEBUG 741 if (debug) 742 (void)printf("<bat in room %d>\n", loc); 743 #endif 744 } 745 746 for (i = 0; i < pit_num; ++i) { 747 do { 748 loc = arc4random_uniform(room_num) + 1; 749 } while (cave[loc].has_a_pit || cave[loc].has_a_bat); 750 /* Above used to be &&; || makes sense but so does just 751 * checking cave[loc].has_a_pit */ 752 cave[loc].has_a_pit = 1; 753 #ifdef DEBUG 754 if (debug) 755 (void)printf("<pit in room %d>\n", loc); 756 #endif 757 } 758 759 wumpus_loc = arc4random_uniform(room_num) + 1; 760 #ifdef DEBUG 761 if (debug) 762 (void)printf("<wumpus in room %d>\n", wumpus_loc); 763 #endif 764 765 do { 766 player_loc = arc4random_uniform(room_num) + 1; 767 } while (player_loc == wumpus_loc || cave[player_loc].has_a_pit || 768 cave[player_loc].has_a_bat); 769 /* Replaced (level == HARD ? 770 * (link_num / room_num < 0.4 ? wump_nearby() : 0) : 0) 771 * with bat/pit checks in initial room. If this is kept there is 772 * a slight chance that no room satisfies all four conditions. 773 */ 774 } 775 776 int 777 getans(const char *prompt) 778 { 779 char buf[20]; 780 781 /* 782 * simple routine to ask the yes/no question specified until the user 783 * answers yes or no, then return 1 if they said 'yes' and 0 if they 784 * answered 'no'. 785 */ 786 for (;;) { 787 (void)printf("%s", prompt); 788 (void)fflush(stdout); 789 if (!fgets(buf, sizeof(buf), stdin)) 790 return(0); 791 if (*buf == 'N' || *buf == 'n') 792 return(0); 793 if (*buf == 'Y' || *buf == 'y') 794 return(1); 795 (void)printf( 796 "I don't understand your answer; please enter 'y' or 'n'!\n"); 797 } 798 /* NOTREACHED */ 799 } 800 801 int 802 bats_nearby(void) 803 { 804 int i; 805 806 /* check for bats in the immediate vicinity */ 807 for (i = 0; i < link_num; ++i) 808 if (cave[cave[player_loc].tunnel[i]].has_a_bat) 809 return(1); 810 return(0); 811 } 812 813 int 814 pit_nearby(void) 815 { 816 int i; 817 818 /* check for pits in the immediate vicinity */ 819 for (i = 0; i < link_num; ++i) 820 if (cave[cave[player_loc].tunnel[i]].has_a_pit) 821 return(1); 822 return(0); 823 } 824 825 int 826 wump_nearby(void) 827 { 828 int i, j; 829 830 /* check for a wumpus within TWO caves of where we are */ 831 for (i = 0; i < link_num; ++i) { 832 if (cave[player_loc].tunnel[i] == wumpus_loc) 833 return(1); 834 for (j = 0; j < link_num; ++j) 835 if (cave[cave[player_loc].tunnel[i]].tunnel[j] == 836 wumpus_loc) 837 return(1); 838 } 839 return(0); 840 } 841 842 void 843 move_wump(void) 844 { 845 wumpus_loc = cave[wumpus_loc].tunnel[arc4random_uniform(link_num)]; 846 #ifdef DEBUG 847 if (debug) 848 (void)printf("Wumpus moved to room %d\n",wumpus_loc); 849 #endif 850 } 851 852 int 853 int_compare(const void *a, const void *b) 854 { 855 return(*(const int *)a < *(const int *)b ? -1 : 1); 856 } 857 858 void 859 instructions(void) 860 { 861 const char *pager; 862 pid_t pid; 863 int status; 864 int fd; 865 866 /* 867 * read the instructions file, if needed, and show the user how to 868 * play this game! 869 */ 870 if (!getans("Instructions? (y-n) ")) 871 return; 872 873 if ((fd = open(_PATH_WUMPINFO, O_RDONLY)) == -1) { 874 (void)printf( 875 "Sorry, but the instruction file seems to have disappeared in a\n\ 876 puff of greasy black smoke! (poof)\n"); 877 return; 878 } 879 880 if (!isatty(1)) 881 pager = "/bin/cat"; 882 else { 883 if (!(pager = getenv("PAGER")) || (*pager == 0)) 884 pager = _PATH_PAGER; 885 } 886 switch (pid = fork()) { 887 case 0: /* child */ 888 if (dup2(fd, 0) == -1) 889 err(1, "dup2"); 890 (void)execl(_PATH_BSHELL, "sh", "-c", pager, (char *)NULL); 891 err(1, "exec sh -c %s", pager); 892 /* NOT REACHED */ 893 case -1: 894 err(1, "fork"); 895 /* NOT REACHED */ 896 default: 897 (void)waitpid(pid, &status, 0); 898 close(fd); 899 break; 900 } 901 } 902 903 void 904 usage(void) 905 { 906 (void)fprintf(stderr, 907 "usage: wump [-ho] [-a arrows] [-b bats] [-p pits] " 908 "[-r rooms] [-t tunnels]\n"); 909 exit(1); 910 } 911 912 /* messages */ 913 void 914 wump_kill(void) 915 { 916 (void)printf( 917 "*ROAR* *chomp* *snurfle* *chomp*!\n\ 918 Much to the delight of the Wumpus, you walk right into his mouth,\n\ 919 making you one of the easiest dinners he's ever had! For you, however,\n\ 920 it's a rather unpleasant death. The only good thing is that it's been\n\ 921 so long since the evil Wumpus cleaned his teeth that you immediately\n\ 922 pass out from the stench!\n"); 923 } 924 925 void 926 wump_walk_kill(void) 927 { 928 (void)printf( 929 "Oh dear. All the commotion has managed to awaken the evil Wumpus, who\n\ 930 has chosen to walk into this very room! Your eyes open wide as they behold\n\ 931 the great sucker-footed bulk that is the Wumpus; the mouth of the Wumpus\n\ 932 also opens wide as the evil beast beholds dinner.\n\ 933 *ROAR* *chomp* *snurfle* *chomp*!\n"); 934 } 935 936 void 937 wump_bat_kill(void) 938 { 939 (void)printf( 940 "Flap, flap. The bats fly you right into the room with the evil Wumpus!\n\ 941 The Wumpus, seeing a fine dinner flying overhead, takes a swipe at you,\n\ 942 and the bats, not wanting to serve as hors d'oeuvres, drop their\n\ 943 soon-to-be-dead weight and take off in the way that only bats flying out\n\ 944 of a very bad place can. As you fall towards the large, sharp, and very\n\ 945 foul-smelling teeth of the Wumpus, you think, \"Man, this is going to hurt.\"\n\ 946 It does.\n"); 947 } 948 949 void 950 kill_wump(void) 951 { 952 (void)printf( 953 "*thwock!* *groan* *crash*\n\n\ 954 A horrible roar fills the cave, and you realize, with a smile, that you\n\ 955 have slain the evil Wumpus and won the game! You don't want to tarry for\n\ 956 long, however, because not only is the Wumpus famous, but the stench of\n\ 957 dead Wumpus is also quite well known--a stench powerful enough to slay the\n\ 958 mightiest adventurer at a single whiff!!\n"); 959 } 960 961 void 962 no_arrows(void) 963 { 964 (void)printf( 965 "\nYou turn and look at your quiver, and realize with a sinking feeling\n\ 966 that you've just shot your last arrow (figuratively, too). Sensing this\n\ 967 with its psychic powers, the evil Wumpus rampages through the cave, finds\n\ 968 you, and with a mighty *ROAR* eats you alive!\n"); 969 } 970 971 void 972 shoot_self(void) 973 { 974 (void)printf( 975 "\n*Thwack!* A sudden piercing feeling informs you that your wild arrow\n\ 976 has ricocheted back and wedged in your side, causing extreme agony. The\n\ 977 evil Wumpus, with its psychic powers, realizes this and immediately rushes\n\ 978 to your side, not to help, alas, but to EAT YOU!\n\ 979 (*CHOMP*)\n"); 980 } 981 982 /* 983 * void 984 * jump(int where) 985 * { 986 * (void)printf( 987 * "\nWith a jaunty step you enter the magic tunnel. As you do, you\n\ 988 * notice that the walls are shimmering and glowing. Suddenly you feel\n\ 989 * a very curious, warm sensation and find yourself in room %d!!\n", where); 990 * } 991 */ 992 993 void 994 pit_kill(void) 995 { 996 (void)printf( 997 "*AAAUUUUGGGGGHHHHHhhhhhhhhhh...*\n\ 998 The whistling sound and updraft as you walked into this room of the\n\ 999 cave apparently weren't enough to clue you in to the presence of the\n\ 1000 bottomless pit. You have a lot of time to reflect on this error as\n\ 1001 you fall many miles to the core of the earth. Look on the bright side;\n\ 1002 you can at least find out if Jules Verne was right...\n"); 1003 } 1004 1005 void 1006 pit_kill_bat(void) 1007 { 1008 (void)printf( 1009 "*AAAUUUUGGGGGHHHHHhhhhhhhhhh...*\n\ 1010 It appears the bats have decided to drop you into a bottomless pit. At\n\ 1011 least, that's what the whistling sound and updraft would suggest. Look on\n\ 1012 the bright side; you can at least find out if Jules Verne was right...\n"); 1013 } 1014 1015 void 1016 pit_survive(void) 1017 { 1018 (void)printf( 1019 "Without conscious thought you grab for the side of the cave and manage\n\ 1020 to grasp onto a rocky outcrop. Beneath your feet stretches the limitless\n\ 1021 depths of a bottomless pit! Rock crumbles beneath your feet!\n"); 1022 } 1023