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