Bug Summary

File:src/games/wump/wump.c
Warning:line 341, column 2
Value stored to 'tunnel_available' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name wump.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/games/wump/obj -resource-dir /usr/local/lib/clang/13.0.0 -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/games/wump/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/games/wump/wump.c
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_ROOM25 25 /* a complex cave */
58
59#define MAX_ROOMS_IN_CAVE250 250
60#define ROOMS_IN_CAVE20 20
61#define MIN_ROOMS_IN_CAVE10 10
62
63#define LINKS_IN_ROOM3 3
64#define NUMBER_OF_ARROWS5 5
65#define PIT_COUNT3 3
66#define BAT_COUNT3 3
67
68#define EASY1 1 /* levels of play */
69#define HARD2 2
70
71/* some macro definitions for cleaner output */
72
73#define plural(n)(n == 1 ? "" : "s") (n == 1 ? "" : "s")
74
75/* simple cave data structure; +1 so we can index from '1' not '0' */
76struct room_record {
77 int tunnel[MAX_LINKS_IN_ROOM25];
78 int has_a_pit, has_a_bat;
79} cave[MAX_ROOMS_IN_CAVE250+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 */
85int player_loc = -1; /* player location */
86int wumpus_loc = -1; /* The Bad Guy location */
87int level = EASY1; /* level of play */
88int arrows_left; /* arrows unshot */
89int oldstyle = 0; /* dodecahedral cave? */
90
91#ifdef DEBUG
92int debug = 0;
93#endif
94
95int pit_num = -1; /* # pits in cave */
96int bat_num = -1; /* # bats */
97int room_num = ROOMS_IN_CAVE20; /* # rooms in cave */
98int link_num = LINKS_IN_ROOM3; /* links per room */
99int arrow_num = NUMBER_OF_ARROWS5; /* arrow inventory */
100
101char answer[20]; /* user input */
102
103int bats_nearby(void);
104void cave_init(void);
105void clear_things_in_cave(void);
106void display_room_stats(void);
107void dodecahedral_cave_init(void);
108int gcd(int, int);
109int getans(const char *);
110void initialize_things_in_cave(void);
111void instructions(void);
112int int_compare(const void *, const void *);
113/* void jump(int); */
114void kill_wump(void);
115int main(int, char **);
116int move_to(const char *);
117void move_wump(void);
118void no_arrows(void);
119void pit_kill(void);
120void pit_kill_bat(void);
121int pit_nearby(void);
122void pit_survive(void);
123int shoot(char *);
124void shoot_self(void);
125int take_action(void);
126__dead__attribute__((__noreturn__)) void usage(void);
127void wump_kill(void);
128void wump_bat_kill(void);
129void wump_walk_kill(void);
130int wump_nearby(void);
131
132
133int
134main(int argc, char *argv[])
135{
136 int c;
137
138 if (pledge("stdio rpath proc exec", NULL((void *)0)) == -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 = HARD2;
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_CAVE10)
170 errx(1,
171 "no self-respecting wumpus would live in such a small cave!");
172 if (room_num > MAX_ROOMS_IN_CAVE250)
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_COUNT3;
197 if (pit_num < 0)
198 pit_num = PIT_COUNT3;
199 }
200
201 if (link_num > MAX_LINKS_IN_ROOM25 ||
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 == HARD2) {
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((void *)0)) == -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\
237There are %d bat%s and %d pit%s scattered throughout the cave, and your\n\
238quiver holds %d custom super anti-evil Wumpus arrows. Good luck.\n",
239 room_num, link_num, bat_num, plural(bat_num)(bat_num == 1 ? "" : "s"), pit_num,
240 plural(pit_num)(pit_num == 1 ? "" : "s"), 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(&__sF[1]));
249 (void)fpurge(stdin(&__sF[0]));
250 if (!fgets(answer, sizeof(answer), stdin(&__sF[0])))
251 break;
252 } while (!take_action());
253 (void)fpurge(stdin(&__sF[0]));
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
269void
270display_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)(arrows_left == 1 ? "" : "s"));
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
299int
300take_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
328int
329move_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;
Value stored to 'tunnel_available' is never read
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(&__sF[1]));
361 if (!fgets(answer, sizeof(answer), stdin(&__sF[0])))
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
426int
427shoot(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((void *)0)) {
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(&__sF[1]));
449 if (!(p = strtok(fgets(answer, sizeof(answer), stdin(&__sF[0])),
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\
473The 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\
499your 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\
509into 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 == EASY1 ? 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
554int
555gcd(int a, int b)
556{
557 int r;
558
559 if (!(r = (a % b)))
560 return(b);
561 return(gcd(b, r));
562}
563
564void
565cave_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;
613try_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
656void
657dodecahedral_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
721void
722clear_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
734void
735initialize_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
781int
782getans(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(&__sF[1]));
794 if (!fgets(buf, sizeof(buf), stdin(&__sF[0])))
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
805int
806bats_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
817int
818pit_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
829int
830wump_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
846void
847move_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
856int
857int_compare(const void *a, const void *b)
858{
859 return(*(const int *)a < *(const int *)b ? -1 : 1);
860}
861
862void
863instructions(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"/usr/share/games/wump.info", O_RDONLY0x0000)) == -1) {
878 (void)printf(
879"Sorry, but the instruction file seems to have disappeared in a\n\
880puff 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"/usr/bin/more";
889 }
890 switch (pid = fork()) {
891 case 0: /* child */
892 if (dup2(fd, 0) == -1)
893 err(1, "dup2");
894 (void)execl(_PATH_BSHELL"/bin/sh", "sh", "-c", pager, (char *)NULL((void *)0));
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
907void
908usage(void)
909{
910 (void)fprintf(stderr(&__sF[2]),
911 "usage: %s [-ho] [-a arrows] [-b bats] [-p pits] "
912 "[-r rooms] [-t tunnels]\n", getprogname());
913 exit(1);
914}
915
916/* messages */
917void
918wump_kill(void)
919{
920 (void)printf(
921"*ROAR* *chomp* *snurfle* *chomp*!\n\
922Much to the delight of the Wumpus, you walk right into his mouth,\n\
923making you one of the easiest dinners he's ever had! For you, however,\n\
924it's a rather unpleasant death. The only good thing is that it's been\n\
925so long since the evil Wumpus cleaned his teeth that you immediately\n\
926pass out from the stench!\n");
927}
928
929void
930wump_walk_kill(void)
931{
932 (void)printf(
933"Oh dear. All the commotion has managed to awaken the evil Wumpus, who\n\
934has chosen to walk into this very room! Your eyes open wide as they behold\n\
935the great sucker-footed bulk that is the Wumpus; the mouth of the Wumpus\n\
936also opens wide as the evil beast beholds dinner.\n\
937*ROAR* *chomp* *snurfle* *chomp*!\n");
938}
939
940void
941wump_bat_kill(void)
942{
943 (void)printf(
944"Flap, flap. The bats fly you right into the room with the evil Wumpus!\n\
945The Wumpus, seeing a fine dinner flying overhead, takes a swipe at you,\n\
946and the bats, not wanting to serve as hors d'oeuvres, drop their\n\
947soon-to-be-dead weight and take off in the way that only bats flying out\n\
948of a very bad place can. As you fall towards the large, sharp, and very\n\
949foul-smelling teeth of the Wumpus, you think, \"Man, this is going to hurt.\"\n\
950It does.\n");
951}
952
953void
954kill_wump(void)
955{
956 (void)printf(
957"*thwock!* *groan* *crash*\n\n\
958A horrible roar fills the cave, and you realize, with a smile, that you\n\
959have slain the evil Wumpus and won the game! You don't want to tarry for\n\
960long, however, because not only is the Wumpus famous, but the stench of\n\
961dead Wumpus is also quite well known--a stench powerful enough to slay the\n\
962mightiest adventurer at a single whiff!!\n");
963}
964
965void
966no_arrows(void)
967{
968 (void)printf(
969"\nYou turn and look at your quiver, and realize with a sinking feeling\n\
970that you've just shot your last arrow (figuratively, too). Sensing this\n\
971with its psychic powers, the evil Wumpus rampages through the cave, finds\n\
972you, and with a mighty *ROAR* eats you alive!\n");
973}
974
975void
976shoot_self(void)
977{
978 (void)printf(
979"\n*Thwack!* A sudden piercing feeling informs you that your wild arrow\n\
980has ricocheted back and wedged in your side, causing extreme agony. The\n\
981evil Wumpus, with its psychic powers, realizes this and immediately rushes\n\
982to 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
997void
998pit_kill(void)
999{
1000 (void)printf(
1001"*AAAUUUUGGGGGHHHHHhhhhhhhhhh...*\n\
1002The whistling sound and updraft as you walked into this room of the\n\
1003cave apparently weren't enough to clue you in to the presence of the\n\
1004bottomless pit. You have a lot of time to reflect on this error as\n\
1005you fall many miles to the core of the earth. Look on the bright side;\n\
1006you can at least find out if Jules Verne was right...\n");
1007}
1008
1009void
1010pit_kill_bat(void)
1011{
1012 (void)printf(
1013"*AAAUUUUGGGGGHHHHHhhhhhhhhhh...*\n\
1014It appears the bats have decided to drop you into a bottomless pit. At\n\
1015least, that's what the whistling sound and updraft would suggest. Look on\n\
1016the bright side; you can at least find out if Jules Verne was right...\n");
1017}
1018
1019void
1020pit_survive(void)
1021{
1022 (void)printf(
1023"Without conscious thought you grab for the side of the cave and manage\n\
1024to grasp onto a rocky outcrop. Beneath your feet stretches the limitless\n\
1025depths of a bottomless pit! Rock crumbles beneath your feet!\n");
1026}