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