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