6415800ae71c67b065e8afb271fed12de8803134
[grue-hunter.git] / gh.pl
1 #!/usr/bin/perl
2 #
3 # This file is part of Grue Hunter.
4 #
5 # Copyright (C) 2013 Jason Self <j@jxself.org>
6 #
7 # Grue Hunter gives you software freedom; you can copy, modify, convey,
8 # and/or redistribute it under the terms of the GNU Affero General Public
9 # License as published by the Free Software Foundation; either version 3 of 
10 # the License, or (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful, but WITHOUT
13 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
14 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License 
15 # for more details.
16 #
17 # You should have received a copy of the GNU Affero General Public License 
18 # along with this program in a file called 'AGPLv3.txt'. If not, see
19 # http://www.gnu.org/licenses/agpl-3.0-standalone.html or write to the: Free
20 # Software Foundation, Inc., 51 Franklin St, Fifth Floor Boston, MA 
21 # 02110-1301, USA.
22
23 use strict;
24 use warnings;
25
26 my $instructions =  <<INSTRUCTIONS;
27 \033[2J
28 \033[0;0H
29 Grue Hunter version 1.0
30 Homepage: http://jxself.org/grue-hunter.shtml
31 Copyright (C) 2013 Jason Self <j\@jxself.org>
32
33 Grue Hunter gives you software freedom; you can copy, modify, convey,
34 and/or redistribute it under the terms of the GNU Affero General Public
35 License as published by the Free Software Foundation; either version 3 of
36 the License, or (at your option) any later version.
37
38 You should have received a copy of the GNU Affero General Public License
39 along with this program in a file called 'AGPLv3.txt'. If not, see
40 http://www.gnu.org/licenses/
41 %
42 \033[2J
43 \033[0;0H
44 In this game you are a brave hunter. You are going to enter an underground 
45 cave system in search of the Grue. If you can capture the Grue and get out 
46 alive you will win the game.
47 %
48 Once in the cave system you can move around or shoot an arrow using the 
49 commands 'move' and 'shoot' and any of the four compass directions. You can 
50 also shorten the commands to 'm' and 's' and the directions to 'n', 's', 'e', 
51 and 'w' for brevity. You may enter 'quit' or 'q' to end the game at any time.
52 %
53 But... There are other things in this cave system. Giant bats can pick you up 
54 and drop you elsewhere. There are also bottomless pits. If you fall into one 
55 of these you will never get out. There is also the Grue itself. Though not an 
56 aggressive creature, it will eat you if you come too close. There are also 
57 earthquakes that move things around (the bats, the bottomless pits, the Grue, 
58 and the exit.)
59 INSTRUCTIONS
60
61 my $space = <<SPACE;
62 The room here slopes downward. Old candles are scattered across the floor.
63 An old fountain sits in the middle of the room. It is covered in rubble and dirt. A toppled statue lies to the right.
64 The walls here are smooth. There is an old anvil here.
65 The walls contain claw marks and some sort of graffiti in a language you cannot read. A crack in the ceiling above the middle of the north wall allows a trickle of water to flow down to the floor. The water pools near the base of the wall, and a rivulet runs along the wall and out into the hall. The water smells fresh.
66 You push a heavy stone door open, revealing a room with pillars lining the wall. Several square holes have been cut into the walls.
67 Stalactites drip down from the ceiling in sharp points while flowstone makes strange shapes on the floor. Rusting spikes line the walls.
68 This room smells of rotting vegetation. Burning torches in iron sconces line the walls.
69 The scent of smoke fills the room. A small hole to your left attracts your attention; but it is too small to be of any concern.
70 A stream of oil flows from one side of the room to the other, in a small rut in the floor. There is a mysterious stain on the wall.
71 This is the center of a narrow passage that connects other rooms. This room is about average size; but is filled with a pungent aroma that is very nauseating.
72 There are cracks in the floor here. It is foggy, making it difficult to see.
73 Thick cobwebs fill the corners of this room and wisps of webbing hang from the ceiling, wavering in a wind you can't feel. You notice a light humming sound.
74 The floor is damp and looks quite slippery. You will need to be very careful here. A haze of smoke hangs in the air.
75 This is a very large room with a large boulder in the center of it. Part of the wall has collapsed into this room.
76 Several pieces of rotten fruit are scatted throughout the area. A hole has been dug into the wall. After looking inside you see that it doesn’t go anywhere.
77 This room is very dusty, and you see footprints set in the dust. There are spiders on the walls.
78 The passageway is very low; but if we crawl we can make it. After coming through the other side you see a stone stairway that goes up to the wall, where it abruptly ends.
79 As you enter the room part of the ceiling collapses but you jump out of the way in time to avoid getting hurt. Duck your head in here; as large rock stalactites hang from the ceiling.
80 This area is covered in rubble and dirt. There is a puddle and scraps of wood here.
81 The floor is covered with sand and pottery shards.
82 The walls have been engraved with geometric patterns. Fog gathers and swirls on the floor, concealing it.
83 There is a salty, wet smell to this area. A small hole in the ceiling lets light from outside through ... But you would never fit through it.
84 There are bones and several piece of rotting wood scattered here.
85 It is misty here. Several empty flasks are scattered throughout the area.
86 Rats run past you, in the direction you just came from. The floor here is covered in perfect hexagonal tiles.
87 There are strange looking yellow stones here. Water drips through the ceiling.
88 Your nostrils are overwhelmed by a sulphurous smell. The ceiling here is damp.
89 Grime and filth smear this room. This area is filled with stalactites and stalagmites.
90 This is a very dirty room; it was partially filled in by the last earthquake that hit. A slight breeze blows through this area.
91 A shallow pit sits in the center of the room. The walls here have been engraved with spiral patterns.
92 This cavern has a high, domed ceiling. There is a mold growing here.
93 A slight, damp breeze blows through this area. This rooms smells of mildew.
94 This area is very dusty. A dry well sits in the center of the room.
95 As you enter this room some small rocks fall from the ceiling. Several broken arrows are scatted about.
96 You are in a small passageway. The walls, ceiling and floor are composed of a smooth black stone. Illuminating your way is a silvery white glow coming from two long grooves midway up each wall that run the length of the passageway.
97 You hear an eerie sound. Jagged steel blades project from cracks in the walls.
98 Someone has scrawled the drawing of a door on the wall. A haze of dust hangs in the air.
99 The walls are covered in slime. There is broken glass on the ground.
100 There is a green slime on the floor. This room is very small; but I think we can make it through ok. 
101 There is a stale smell here. Your steps echo here.
102 This large room is illuminated by a low-burning fire pit. There is an iron sconce on the wall, but the torch has been removed.
103 A rivulet of water slowly trickles from a hole in the wall. A narrow pit covered by iron bars lies in the center of the room.
104 Several empty bottles lie on the floor. Water drips from the ceiling, landing on your head.
105 You are in a low depression in the center of a medium-sized room. A flurry of bats suddenly fly past you as you enter. Their screeching fills your ears.
106 Someone has left a lighted torch on the wall and it illuminates your passage. A liquid-filled pit sits in the center of the room and is so murky that you can't see its bottom. The room smells sour.
107 This area is only a few feet high, but crawling on your stomach you make it through. You notice centipedes crawling along the wall.
108 A rattling noise abruptly stops when you enter. Black mold grows on the walls and parts of the floor.
109 You are in a small room with rocks and debris scattered everywhere. Part of the here ceiling has collapsed.
110 A huge iron cage lies on its side in this room, and its gate rests open on the floor. A broken chain lies on the floor. This room has sour odor, like something rotten or fermented.
111 An iron chandelier hangs from the ceiling. A old wooden barrel lies on its side, mostly collapsed.
112 SPACE
113
114 # Constants
115 my $NORTH = 1;
116 my $SOUTH = 2;
117 my $EAST = 3;
118 my $WEST = 4;
119
120 # Update this constant to increase the number of rooms in the game.
121 my $MAX_ROOMS = 50;
122
123 # Update this constant if more room descriptions are added.
124 my $MAX_ROOM_DESCRIPTIONS = 50;
125
126 my $MAX_LINK_COUNT = 4;
127 my $EARTHQUAKE = 3;
128 my $EARTHQUAKE_CHANCE = 14;
129 my $EVEN_CHANCE = 2;
130 my $PIT_CHANCE = 32;
131
132 my $SET = 1;
133 my $RESET = 0;
134
135 my $CLOSED = -1;
136 my $GRUE = -2;
137 my $EXIT = -3;
138 my $BAT1 = -4;
139 my $BAT2 = -5;
140 my $PIT1 = -6;
141 my $PIT2 = -7;
142
143 # Game State
144 my $grue_location = $CLOSED;
145 my $exit_location = $CLOSED;
146 my $bat1_location = $CLOSED;
147 my $bat2_location = $CLOSED;
148 my $pit1_location = $CLOSED;
149 my $pit2_location = $CLOSED;
150 my $player_location = $CLOSED;
151 my $grue_bagged = 0;
152
153 # Game Space
154 my @rooms = ();
155
156 sub randomize_rooms {
157         my @room_descriptions = split(/\n/, $space);
158         my %random_hash = ();
159         my $random_room = 0;
160         my $random_room_description = 0;
161         
162         for (my $i = 0; $i < $MAX_ROOMS;) {
163                 $random_room = int(rand($MAX_ROOMS));
164                 $random_room_description = int(rand($MAX_ROOM_DESCRIPTIONS));
165                 
166                 if (!$random_hash{$random_room}) {
167                         $random_hash{$random_room} = 1;
168                         my %room = ();
169                         $room{'description'} = $room_descriptions[$random_room_description];
170                         $room{$NORTH} = $CLOSED;
171                         $room{$SOUTH} = $CLOSED;
172                         $room{$EAST} = $CLOSED;
173                         $room{$WEST} = $CLOSED;
174                         $room{'link_count'} = 0;
175                         $rooms[$i] = \%room;
176                         $i++;
177                 }
178         }
179 }
180
181 sub connect_rooms {
182         my $current_link_count = 0;
183         my $random_link_count = 0;
184         my $rooms_left_to_link = 0;
185         my $room_direction_state = 0;
186         my $next_room = 0;
187         
188         for (my $i = 0; $i < $MAX_ROOMS;) {
189         
190                 # Take into consideration current link count and get random link count.
191                 $current_link_count = $rooms[$i] -> {'link_count'};
192                 if ($current_link_count < $MAX_LINK_COUNT) {
193                         $random_link_count = int(rand($MAX_LINK_COUNT - $current_link_count)) + 1;
194                         
195                         # Check that there are enough rooms left to link. If there are not
196                         # then substitute the random link count with the rooms left to link.
197                         $rooms_left_to_link = $MAX_ROOMS - $i;
198                         if ($random_link_count >= $rooms_left_to_link) {
199                                 $random_link_count = $rooms_left_to_link;
200                         }
201                 }
202                 
203                 # If the room is fully linked then set the random link count to 0.
204                 else {
205                         $random_link_count = 0;
206                 }
207                 
208                 # For each room create links upto a random link count by going through
209                 # the vacant compass directions.
210                 for (my $j = 0; $j < $random_link_count; $j++) {
211                         for (my $k = 1; $k < 5; $k++) {
212                                 if ($i < $MAX_ROOMS) {
213                                         $room_direction_state = $rooms[$i] -> {$k};
214                                         if ($room_direction_state == $CLOSED) {
215                                                 $next_room = $i + 1;
216                                                 if ($next_room < $MAX_ROOMS) {
217                                                         $rooms[$i] -> {$k} = $next_room;
218                                                         $rooms[$next_room] -> {opposite($k)} = $i;
219                                                 }
220                                                 $i = $next_room;
221                                         }
222                                 }
223                         }
224                 }
225         }
226 }
227
228 sub place_directional_object {
229         my $room_number = shift;
230         my $object = shift;
231         my $direction;
232         
233         # Get a random direction and set the object in a random room direction.
234         $direction = int(rand(3)) + 1;
235         $rooms[$room_number] -> {$direction} = $object;
236         
237         # Update the room's link count
238         ($rooms[$room_number] -> {'link_count'})++;
239
240         if ($object == $GRUE) {
241                 $grue_location = $room_number;
242         }
243         elsif ($object == $EXIT) {
244                 $exit_location = $room_number;
245         }
246         elsif ($object == $PIT1) {
247                 $pit1_location = $room_number;
248         }
249         elsif ($object == $PIT2) {
250                 $pit2_location = $room_number;
251         }
252 }
253
254 sub get_random_rooms {
255         my $number_of_rooms = shift;
256         my $random_num = 0;
257         my %random_hash = ();
258         my @room_numbers = ();
259
260         for (my $i = 0; $i < $number_of_rooms;) {
261                 $random_num = int(rand($MAX_ROOMS));
262                 
263                 if (!$random_hash{$random_num}) {
264                         $random_hash{$random_num} = 1;
265                         $room_numbers[$i] = $random_num;
266                         $i++;
267                 }
268         }
269         return \@room_numbers;
270 }
271
272 sub set_state {
273         my $set = shift;
274         my $number_of_rooms;
275         my $room_numbers;
276         
277         if ($grue_bagged) {
278                 $number_of_rooms = 5;
279         }
280         else {
281                 $number_of_rooms = 6;
282         }
283         
284         $room_numbers = get_random_rooms($number_of_rooms);
285         
286         # Bats are non-directional objects and are placed in rooms. Since they are
287         # non-directional they do not occupy a compass direction and do not add to the
288         # room's link count. They are simply assigned to the Game State variables.
289         $bat1_location = @$room_numbers[0];
290         $bat2_location = @$room_numbers[1];
291         
292         # The Grue, Pits and Exit are directional objects, placed in rooms. They
293         # are directional they occupy compass dirctions and are added to the room's
294         # link count. In addition they are assigned to the Game State variables. The
295         # Grue is only placed in a room again if it has not been bagged.
296         place_directional_object(@$room_numbers[2], $PIT1);
297         place_directional_object(@$room_numbers[3], $PIT2);
298         place_directional_object(@$room_numbers[4], $EXIT);
299         if (!$grue_bagged) {
300                 place_directional_object(@$room_numbers[5], $GRUE);
301         }
302         # If the game state is being set for the first time, then the player's
303         # location is set to the same location as the Exit.
304         if ($set) {
305                 $player_location = @$room_numbers[4];
306         }
307 }
308
309 sub opposite {
310         my $direction = shift;
311         
312         if ($direction == $NORTH) {
313                 return $SOUTH;
314         }
315         elsif ($direction == $SOUTH) {
316                 return $NORTH;
317         }
318         elsif ($direction == $EAST) {
319                 return $WEST;
320         }
321         elsif ($direction == $WEST) {
322                 return $EAST;
323         }
324         else {
325                 return 0;
326         }
327 }
328
329 sub action_handler {
330         my ($action, $sub_action) = @_;
331         my $action_success = 0;
332         
333         if (defined $action) {
334                 $action = lc($action);
335         
336                 # If the sub action (direction) is defined.
337                 if (defined $sub_action) {
338                         $sub_action = lc($sub_action);
339                         
340                         # If sub action (direction) is a valid direction.
341                         if (is_direction($sub_action)) {
342                                 if ($sub_action eq 'q' || $action eq 'quit') {
343                                         quit();
344                                 }
345                                 elsif ($action eq 'm' || $action eq 'move') {
346                                         move_player($sub_action);
347                                         $action_success = 1;
348                                 }
349                                 elsif ($action eq 's' || $action eq 'shoot') {
350                                         shoot($sub_action);
351                                         $action_success = 1; 
352                                 }
353                         }
354                         
355                         # If the sub action (direction) is not a valid direction.
356                         else {
357                                 print "Enter 'N', 'S', 'E' OR 'W'.\n";
358                                 if ($action eq 'm' || $action eq 'move') {
359                                         move_player();
360                                 }
361                                 elsif ($action eq 's' || $action eq 'shoot') {
362                                         shoot();
363                                 }
364                         }
365                         
366                         if ($action_success) {
367                                 my $random_num = 0;
368                                 $random_num = int(rand($EARTHQUAKE_CHANCE));
369                                 
370                                 if ($random_num == $EARTHQUAKE) {
371                                         print "\n<<<EARTHQUAKE>>>\n\n";
372                                         randomize_rooms();
373                                         set_state($RESET);
374                                         connect_rooms();
375                                 }
376                                 
377                                 describe_location();
378                         }
379                 }
380                 # If the sub action (direction) is not defined.
381                 else {
382                         if ($action eq 'm' || $action eq 'move') {
383                                 move_player();
384                         }
385                         elsif ($action eq 's' || $action eq 'shoot') {
386                                 shoot();
387                         }
388                         elsif ($action eq 'q' || $action eq 'quit') {
389                                 quit();
390                         }
391                 }
392         }
393 }
394
395 sub display_text {
396         $| = 1;
397         my $text = shift;
398         for my $word (split/%/, $text) {
399                 print $word."\n";
400                 print 'Press enter when ready to continue: ';
401                 <STDIN>;
402         }
403         print "\033[2J";
404         print "\033[0;0H";
405         print "\t\t\t\t".'***********************'."\n";
406         print "\t\t\t\t".'***                 ***'."\n";
407         print "\t\t\t\t".'***   GRUE HUNTER   ***'."\n";
408         print "\t\t\t\t".'***                 ***'."\n";
409         print "\t\t\t\t".'***********************'."\n\n";
410 }
411
412 sub read_input {
413         my $action;
414         my @actions;
415         $action = <>;
416         @actions = split(/\s+/, $action);
417         return @actions;
418 }
419
420 sub is_direction {
421         my $direction = shift;
422         if (defined $direction) {
423                 return (
424                         $direction eq 'n' or
425                         $direction eq 'north' or
426                         $direction eq 's' or
427                         $direction eq 'south' or
428                         $direction eq 'e' or
429                         $direction eq 'east' or
430                         $direction eq 'w' or
431                         $direction eq 'west' or
432                         $direction eq 'q' or
433                         $direction eq 'quit'
434                 );
435         }
436         return '';
437 }
438
439 sub direction {
440         my $direction = shift;
441         
442         if ($direction == $NORTH) {
443                 return 'north';
444         }
445         elsif ($direction == $SOUTH) {
446                 return 'south';
447         }
448         elsif ($direction == $EAST) {
449                 return 'east';
450         }
451         elsif ($direction == $WEST) {
452                 return 'west';
453         }
454         else {
455                 return '';
456         }
457 }
458
459 sub move_player {
460         my $sub_action = shift;
461         my $direction;
462         my $room_direction;
463         
464         if (defined $sub_action) {
465                 if ($sub_action eq 'n' || $sub_action eq 'north') {
466                         $direction = $NORTH;
467                 }
468                 elsif ($sub_action eq 's' || $sub_action eq 'south') {
469                         $direction = $SOUTH;
470                 }
471                 elsif ($sub_action eq 'e' || $sub_action eq 'east') {
472                         $direction = $EAST;
473                 }
474                 elsif ($sub_action eq 'w' || $sub_action eq 'west') {
475                         $direction = $WEST;
476                 }
477                 
478                 $room_direction = $rooms[$player_location] -> {$direction};
479                 
480                 # If the room that the player wants to move to is a room.
481                 if ($room_direction > $CLOSED) {
482                 
483                         # Check if the room has a bat.
484                         if ($room_direction == $bat1_location || $room_direction == $bat2_location) {
485                                 print "Bats have you now.\n";
486                                 print "They're lifting you up.\n";
487                                 print "Ohhhh, where are we now?\n";
488                                 
489                                 my $number_of_rooms = 2;
490                                 my $room_numbers = get_random_rooms($number_of_rooms);
491                                 
492                                 # Change player location (to where the bat dropped the player).
493                                 $player_location = @$room_numbers[0];
494                                 
495                                 # Change location of offending bat.
496                                 if ($room_direction == $bat1_location) {
497                                         $bat1_location = @$room_numbers[1];
498                                 }
499                                 else {
500                                         $bat2_location = @$room_numbers[1];
501                                 }
502                         }
503                         
504                         # If free of bats, go ahead and move into the room.
505                         else {
506                                 print "OK...\n";
507                                 $player_location = $room_direction;
508                         }
509                 }
510                 elsif ($room_direction == $CLOSED) {
511                         print "You cannot go that way.\n";
512                 }
513                 elsif ($room_direction == $GRUE) {
514                         print "You bumped into the Grue.\n";
515                         print "He ate you before you could move.\n";
516                         quit();
517                 }
518                 elsif ($room_direction == $EXIT) {
519                         if ($grue_bagged) {
520                                 print "You have reached the exit with your Grue.\n";
521                                 print "Your supper will be filling tonight for sure!\n";
522                         }
523                         else {
524                                 print "You have reached the exit without any Grue.\n";
525                                 print "You are sure to starve.\n";
526                         }
527                         quit();
528                 }
529                 elsif ($room_direction < $PIT1) {
530                         my $pit_chance = int(rand($PIT_CHANCE));
531                         if ($pit_chance) {
532                                 print "This room contains a bottomless pit. Part of the ground around it gave way when you entered and you almost fell in.\n";
533                                 print "Be more careful next time!\n";
534                                 $player_location = $room_direction;
535                         }
536                         else {
537                                 print "You fell into a bottomless pit.\n";
538                                 print "You fell a loooooong way...\n";
539                                 quit();
540                         }
541                 }
542         }
543         else {
544                 my @actions;
545                 print "Move which way?\n";
546                 @actions = read_input();
547                 action_handler('m', $actions[0]);
548         }
549 }
550
551 sub shoot {
552         my $sub_action = shift;
553         my $direction;
554         my $room_direction;
555         
556         if (defined $sub_action) {
557                 if ($sub_action eq 'n' || $sub_action eq 'north') {
558                         $direction = $NORTH;
559                 }
560                 elsif ($sub_action eq 's' || $sub_action eq 'south') {
561                         $direction = $SOUTH;
562                 }
563                 elsif ($sub_action eq 'e' || $sub_action eq 'east') {
564                         $direction = $EAST;
565                 }
566                 elsif ($sub_action eq 'w' || $sub_action eq 'west') {
567                         $direction = $WEST;
568                 }
569                 
570                 $room_direction = $rooms[$player_location] -> {$direction};
571                 
572                 # If the arrow was shot in a direction without the Grue.
573                 if ($room_direction == $CLOSED) {
574                         print "Clunk!\n";
575                         print "The arrow bounced off the wall.\n";
576                 }
577                 # If the arrow was shot in a direction of the Grue.
578                 elsif ($room_direction == $GRUE) {
579                         my $shot = int(rand($EVEN_CHANCE));
580                         if ($shot) {
581                                 print "You caught the Grue!\n";
582                                 print "Now to find a way out...\n";
583                                 $grue_bagged++;
584                                 
585                                 # Remove the Grue from the Room once bagged. Set the Grue
586                                 # location to an undefined state (the Grue object -2).
587                                 $rooms[$player_location] -> {$direction} = $CLOSED;
588                                 ($rooms[$player_location] -> {'link_count'})--;
589                                 $grue_location = $GRUE;
590                         }
591                         else {
592                                 print "The arrow missed the Grue.\n";
593                         }
594                 }
595                 else {
596                         print "The arrow missed the Grue.\n";
597                 }
598         }
599         else {
600                 my @actions;
601                 print "Shoot which way?\n";
602                 @actions = read_input();
603                 action_handler('s', $actions[0]);
604         }
605 }
606
607 sub describe_location {
608         my $room_direction;
609         
610         print $rooms[$player_location] -> {'description'} . "\n";
611 # print "Player Location: $player_location\n";
612 # print "Grue Location: $grue_location\n";
613 # print "Bat 1 Location: $bat1_location\n";
614 # print "Bat 2 Location: $bat2_location\n";
615 # print "Pit 1 Location: $pit1_location\n";
616 # print "Pit 2 Location: $pit2_location\n";
617 # print "Exit Location: $exit_location\n";
618         
619         print 'I see passages to the ';
620         my @passages;
621         for (my $i = 1; $i < 5; $i++) {
622                 $room_direction = $rooms[$player_location] -> {$i};
623                 if ($room_direction != $CLOSED) {
624                         push(@passages, direction($i));
625                 }
626         }
627         
628         for (my $i = 0; $i < $#passages + 1; $i++) {
629                 if ($i == 0) {
630                         print $passages[$i];
631                 }
632                 elsif ($i < $#passages) {
633                         print ', ' . $passages[$i];
634                 }
635                 elsif ($i == $#passages) {
636                         print ' and ' . $passages[$i];
637                 }
638         }
639         print ". Choose wisely.\n";
640         
641         # If the Grue is in the player's current location.
642         if ($player_location == $grue_location) {
643                 print "I smell the Grue!\n";
644         }
645         # If either pit is in the player's current location.
646         elsif ($player_location == $pit1_location || $player_location == $pit2_location) {
647                 print "I feel a draft. A bottomless pit must be nearby. Be careful not to fall in.\n";
648         }
649         # If the Exit is in the player's current location, tell the player which
650         # direction the Exit is located.
651         elsif ($player_location == $exit_location) {
652                 for (my $i = 1; $i < 5; $i++) {
653                         $room_direction = $rooms[$player_location] -> {$i};
654                         if ($room_direction == $EXIT) {
655                                 print 'The exit is to the ' . direction($i) . ".\n";
656                         }
657                 }
658         }
659         # Check the adjacent rooms for the Exit, Grue, Bats and Pits. Here
660         # check whether the adjacent rooms are equivalent to the game state
661         # variables for the Exit, Grue, Bats and Pits.
662         else {
663                 for (my $i = 1; $i < 5; $i++) {
664                         $room_direction = $rooms[$player_location] -> {$i};
665                         if ($room_direction > $CLOSED) {
666                                 if ($room_direction == $exit_location) {
667                                         print "Exit nearby...\n";
668                                 }
669                                 elsif ($room_direction == $grue_location) {
670                                         print "I smell the Grue!\n";
671                                 }
672                                 # If the room adjacent contains one of the two bats then warn the player.
673                                 elsif ($room_direction == $bat1_location || $room_direction == $bat2_location) {
674                                         print "I hear bats flying around in the distance. Be careful or they may grab you and carry you away.\n";
675                                 }
676                                 # If the room adjacent contains one of the two pits then warn the player.
677                                 elsif ($room_direction == $pit1_location || $room_direction == $pit2_location) {
678                                         print "I feel a draft. A bottomless pit must be nearby. Be careful not to fall in.\n";
679                                 }
680                         }
681                 }
682         }
683 }
684
685 sub quit {
686         exit;
687 }
688
689 sub main {
690         my @actions;
691         
692         display_text($instructions);
693         randomize_rooms();
694         set_state($SET);
695         connect_rooms();
696         describe_location();
697         while (1) {
698                 print "Move or shoot?\n";
699                 @actions = read_input();
700                 action_handler(@actions);
701         }
702 }
703
704 main();