line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Games::Wumpus::Cave; |
2
|
|
|
|
|
|
|
|
3
|
4
|
|
|
4
|
|
55619
|
use 5.010; |
|
4
|
|
|
|
|
15
|
|
|
4
|
|
|
|
|
171
|
|
4
|
|
|
|
|
|
|
|
5
|
4
|
|
|
4
|
|
23
|
use strict; |
|
4
|
|
|
|
|
9
|
|
|
4
|
|
|
|
|
140
|
|
6
|
4
|
|
|
4
|
|
26
|
use warnings; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
167
|
|
7
|
4
|
|
|
4
|
|
20
|
no warnings 'syntax'; |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
320
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
our $VERSION = '2009112401'; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
# |
12
|
|
|
|
|
|
|
# Cave for the wumpus game. |
13
|
|
|
|
|
|
|
# |
14
|
|
|
|
|
|
|
# Cave will contain rooms, and connections to various rooms. |
15
|
|
|
|
|
|
|
# Rooms may contain hazards: wumpus, bats, pits. |
16
|
|
|
|
|
|
|
# |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
# |
19
|
|
|
|
|
|
|
# Default layout is the one of a dodecahedron: vertices are rooms, |
20
|
|
|
|
|
|
|
# edges are tunnels. |
21
|
|
|
|
|
|
|
# |
22
|
|
|
|
|
|
|
|
23
|
4
|
|
|
4
|
|
1080
|
use Games::Wumpus::Constants; |
|
4
|
|
|
|
|
10
|
|
|
4
|
|
|
|
|
727
|
|
24
|
4
|
|
|
4
|
|
1860
|
use Games::Wumpus::Room; |
|
4
|
|
|
|
|
11
|
|
|
4
|
|
|
|
|
128
|
|
25
|
4
|
|
|
4
|
|
116
|
use Hash::Util::FieldHash qw [fieldhash]; |
|
4
|
|
|
|
|
23
|
|
|
4
|
|
|
|
|
207
|
|
26
|
4
|
|
|
4
|
|
21
|
use List::Util qw [shuffle]; |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
7915
|
|
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
fieldhash my %rooms; # List of rooms. |
29
|
|
|
|
|
|
|
fieldhash my %wumpus; # Location of the wumpus. |
30
|
|
|
|
|
|
|
fieldhash my %start; # Start location. |
31
|
|
|
|
|
|
|
fieldhash my %location; # Location of the player. |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
# |
34
|
|
|
|
|
|
|
# Accessors |
35
|
|
|
|
|
|
|
# |
36
|
2
|
|
|
2
|
1
|
822
|
sub rooms {@{$rooms {$_ [0]}}} |
|
2
|
|
|
|
|
12
|
|
37
|
20
|
|
|
20
|
1
|
13190
|
sub room { $rooms {$_ [0]} [$_ [1] - 1]} |
38
|
0
|
|
|
0
|
1
|
0
|
sub random_room { $rooms {$_ [0]} [rand @{$rooms {$_ [0]}}]} |
|
0
|
|
|
|
|
0
|
|
39
|
|
|
|
|
|
|
|
40
|
6
|
|
|
6
|
1
|
34
|
sub location { $location {$_ [0]}} |
41
|
1
|
|
|
1
|
1
|
5
|
sub set_location { $location {$_ [0]} = $_ [1]; $_ [0]} |
|
1
|
|
|
|
|
3
|
|
42
|
|
|
|
|
|
|
|
43
|
0
|
|
|
0
|
1
|
0
|
sub wumpus { $wumpus {$_ [0]}} |
44
|
2
|
|
|
2
|
1
|
9
|
sub set_wumpus { $wumpus {$_ [0]} = $_ [1]; $_ [0]} |
|
2
|
|
|
|
|
4
|
|
45
|
|
|
|
|
|
|
|
46
|
16
|
|
|
16
|
1
|
1135
|
sub start { $start {$_ [0]}} |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# |
49
|
|
|
|
|
|
|
# Construction |
50
|
|
|
|
|
|
|
# |
51
|
2
|
|
|
2
|
1
|
1241
|
sub new {bless \do {my $var} => shift} |
|
2
|
|
|
|
|
14
|
|
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
sub init { |
54
|
2
|
|
|
2
|
1
|
6
|
my $self = shift; |
55
|
2
|
|
|
|
|
5
|
my %args = @_; |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
# |
58
|
|
|
|
|
|
|
# Classical layout. |
59
|
|
|
|
|
|
|
# |
60
|
2
|
|
|
|
|
12
|
$self -> _create_rooms (scalar @CLASSICAL_LAYOUT); |
61
|
2
|
|
|
|
|
14
|
$self -> _classical_layout (%args); |
62
|
|
|
|
|
|
|
|
63
|
2
|
|
|
|
|
9
|
$self -> _name_rooms (%args); |
64
|
2
|
|
|
|
|
9
|
$self -> _create_hazards (%args); |
65
|
|
|
|
|
|
|
|
66
|
2
|
50
|
|
|
|
7
|
if ($::DEBUG) { |
67
|
0
|
|
|
|
|
0
|
my %h; |
68
|
0
|
|
|
|
|
0
|
foreach my $room (@{$rooms {$self}}) { |
|
0
|
|
|
|
|
0
|
|
69
|
0
|
0
|
|
|
|
0
|
if ($room -> has_hazard ($WUMPUS)) { |
70
|
0
|
|
|
|
|
0
|
push @{$h {Wumpus}} => $room -> name; |
|
0
|
|
|
|
|
0
|
|
71
|
|
|
|
|
|
|
} |
72
|
0
|
0
|
|
|
|
0
|
if ($room -> has_hazard ($BAT)) { |
73
|
0
|
|
|
|
|
0
|
push @{$h {Bat}} => $room -> name; |
|
0
|
|
|
|
|
0
|
|
74
|
|
|
|
|
|
|
} |
75
|
0
|
0
|
|
|
|
0
|
if ($room -> has_hazard ($PIT)) { |
76
|
0
|
|
|
|
|
0
|
push @{$h {Pit}} => $room -> name; |
|
0
|
|
|
|
|
0
|
|
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
} |
79
|
0
|
|
|
|
|
0
|
local $, = " "; |
80
|
0
|
|
|
|
|
0
|
say STDERR "Wumpus in", @{$h {Wumpus}}; |
|
0
|
|
|
|
|
0
|
|
81
|
0
|
|
|
|
|
0
|
say STDERR "Bats in", @{$h {Bat}}; |
|
0
|
|
|
|
|
0
|
|
82
|
0
|
|
|
|
|
0
|
say STDERR "Pits in", @{$h {Pit}}; |
|
0
|
|
|
|
|
0
|
|
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
|
85
|
2
|
|
|
|
|
7
|
$self; |
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
# |
89
|
|
|
|
|
|
|
# Create the given number of rooms. |
90
|
|
|
|
|
|
|
# Note that the rooms aren't named here, nor are either exits or hazards set. |
91
|
|
|
|
|
|
|
# |
92
|
|
|
|
|
|
|
sub _create_rooms { |
93
|
2
|
|
|
2
|
|
5
|
my $self = shift; |
94
|
2
|
|
|
|
|
3
|
my $rooms = shift; |
95
|
|
|
|
|
|
|
|
96
|
2
|
|
|
|
|
8
|
$rooms {$self} = [map {Games::Wumpus::Room -> new -> init} 1 .. $rooms]; |
|
40
|
|
|
|
|
183
|
|
97
|
|
|
|
|
|
|
|
98
|
2
|
|
|
|
|
6
|
$self; |
99
|
|
|
|
|
|
|
} |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
# |
102
|
|
|
|
|
|
|
# Create the classical layout |
103
|
|
|
|
|
|
|
# |
104
|
|
|
|
|
|
|
sub _classical_layout { |
105
|
2
|
|
|
2
|
|
4
|
my $self = shift; |
106
|
|
|
|
|
|
|
|
107
|
2
|
|
|
|
|
10
|
for (my $i = 0; $i < @CLASSICAL_LAYOUT; $i ++) { |
108
|
40
|
|
|
|
|
34
|
foreach my $exit (@{$CLASSICAL_LAYOUT [$i]}) { |
|
40
|
|
|
|
|
554
|
|
109
|
120
|
|
|
|
|
395
|
$rooms {$self} [$i] -> add_exit ($rooms {$self} [$exit]); |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
|
113
|
2
|
|
|
|
|
5
|
$self; |
114
|
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
# |
118
|
|
|
|
|
|
|
# Randomly name the rooms; then store them in order. |
119
|
|
|
|
|
|
|
# |
120
|
|
|
|
|
|
|
sub _name_rooms { |
121
|
2
|
|
|
2
|
|
6
|
my $self = shift; |
122
|
2
|
|
|
|
|
5
|
my %args = @_; |
123
|
|
|
|
|
|
|
|
124
|
2
|
|
|
|
|
3
|
my $rooms = @{$rooms {$self}}; |
|
2
|
|
|
|
|
5
|
|
125
|
2
|
|
|
|
|
22
|
my @names = 1 .. $rooms; |
126
|
2
|
50
|
|
|
|
10
|
@names = shuffle @names if $args {shuffle_names}; |
127
|
|
|
|
|
|
|
|
128
|
2
|
|
|
|
|
13
|
for (my $i = 0; $i < @names; $i ++) { |
129
|
40
|
|
|
|
|
120
|
$rooms {$self} [$i] -> set_name ($names [$i]); |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
|
132
|
2
|
|
|
|
|
4
|
$rooms {$self} = [sort {$a -> name <=> $b -> name} @{$rooms {$self}}]; |
|
38
|
|
|
|
|
83
|
|
|
2
|
|
|
|
|
14
|
|
133
|
|
|
|
|
|
|
|
134
|
2
|
|
|
|
|
9
|
$self; |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
# |
138
|
|
|
|
|
|
|
# Assign hazards to rooms. Initially, no room will have more than one hazard. |
139
|
|
|
|
|
|
|
# This method also assigns the start location (hazard free). |
140
|
|
|
|
|
|
|
# |
141
|
|
|
|
|
|
|
sub _create_hazards { |
142
|
2
|
|
|
2
|
|
5
|
my $self = shift; |
143
|
|
|
|
|
|
|
|
144
|
2
|
|
|
|
|
4
|
my @rooms = shuffle @{$rooms {$self}}; |
|
2
|
|
|
|
|
103
|
|
145
|
|
|
|
|
|
|
|
146
|
2
|
|
|
|
|
4
|
my $wumpus_room = pop @rooms; |
147
|
2
|
|
|
|
|
10
|
$wumpus_room -> set_hazard ($WUMPUS); |
148
|
|
|
|
|
|
|
|
149
|
2
|
|
|
|
|
9
|
$self -> set_wumpus ($wumpus_room); |
150
|
|
|
|
|
|
|
|
151
|
2
|
|
|
|
|
12
|
(pop @rooms) -> set_hazard ($PIT) for 1 .. $NR_OF_PITS; |
152
|
2
|
|
|
|
|
10
|
(pop @rooms) -> set_hazard ($BAT) for 1 .. $NR_OF_BATS; |
153
|
|
|
|
|
|
|
|
154
|
2
|
|
|
|
|
8
|
$start {$self} = pop @rooms; |
155
|
|
|
|
|
|
|
|
156
|
2
|
|
|
|
|
6
|
$self; |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
# |
161
|
|
|
|
|
|
|
# Describe the room the player is currently in. |
162
|
|
|
|
|
|
|
# |
163
|
|
|
|
|
|
|
sub describe { |
164
|
4
|
|
|
4
|
1
|
23
|
my $self = shift; |
165
|
|
|
|
|
|
|
|
166
|
4
|
|
|
|
|
7
|
my $text; |
167
|
|
|
|
|
|
|
|
168
|
4
|
|
|
|
|
13
|
my $room = $self -> location; |
169
|
|
|
|
|
|
|
|
170
|
4
|
|
|
|
|
17
|
$text = "You are in room " . $room -> name . ".\n"; |
171
|
4
|
100
|
|
|
|
17
|
$text .= "I smell a Wumpus!\n" if $room -> near_hazard ($WUMPUS); |
172
|
4
|
100
|
|
|
|
14
|
$text .= "I feel a draft.\n" if $room -> near_hazard ($PIT); |
173
|
4
|
100
|
|
|
|
13
|
$text .= "Bats nearby!\n" if $room -> near_hazard ($BAT); |
174
|
|
|
|
|
|
|
|
175
|
12
|
|
|
|
|
33
|
$text .= "Tunnels lead to " . join " ", sort {$a <=> $b} |
|
12
|
|
|
|
|
25
|
|
176
|
4
|
|
|
|
|
15
|
map {$_ -> name} $room -> exits; |
177
|
4
|
|
|
|
|
8
|
$text .= ".\n"; |
178
|
|
|
|
|
|
|
|
179
|
4
|
|
|
|
|
11
|
$text; |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
# |
184
|
|
|
|
|
|
|
# Return whether player can move from current destination to new location. |
185
|
|
|
|
|
|
|
# |
186
|
|
|
|
|
|
|
# If the current location has an exit with the given name, then yes. |
187
|
|
|
|
|
|
|
# |
188
|
|
|
|
|
|
|
sub can_move_to { |
189
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
190
|
0
|
|
|
|
|
|
my $new = shift; |
191
|
|
|
|
|
|
|
|
192
|
0
|
0
|
|
|
|
|
$self -> location -> exit_by_name ($new) ? 1 : 0; |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
# |
197
|
|
|
|
|
|
|
# Move the player to a new location. Return the hazards encountered. |
198
|
|
|
|
|
|
|
# Since bats may move the player, encountering a new hazard, more |
199
|
|
|
|
|
|
|
# than one hazard may be encountered. |
200
|
|
|
|
|
|
|
# |
201
|
|
|
|
|
|
|
sub move { |
202
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
203
|
0
|
|
|
|
|
|
my $new = shift; |
204
|
|
|
|
|
|
|
|
205
|
0
|
|
|
|
|
|
my @hazards; |
206
|
|
|
|
|
|
|
|
207
|
0
|
|
|
|
|
|
$self -> set_location ($self -> room ($new)); |
208
|
|
|
|
|
|
|
|
209
|
0
|
0
|
|
|
|
|
if ($self -> location -> has_hazard ($WUMPUS)) { |
210
|
|
|
|
|
|
|
# Death. |
211
|
0
|
|
|
|
|
|
return $WUMPUS; |
212
|
|
|
|
|
|
|
} |
213
|
0
|
0
|
|
|
|
|
if ($self -> location -> has_hazard ($PIT)) { |
214
|
|
|
|
|
|
|
# Death. |
215
|
0
|
|
|
|
|
|
return $PIT; |
216
|
|
|
|
|
|
|
} |
217
|
0
|
0
|
|
|
|
|
if ($self -> location -> has_hazard ($BAT)) { |
218
|
|
|
|
|
|
|
# Moved. |
219
|
0
|
|
|
|
|
|
return $BAT, $self -> move ($self -> random_room -> name); |
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
# Nothing special. |
223
|
0
|
|
|
|
|
|
return; |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
# |
228
|
|
|
|
|
|
|
# Shoot an arrow. Return the first thing hit (ends shot). |
229
|
|
|
|
|
|
|
# If a tunnel doesn't exist, pick something at random. |
230
|
|
|
|
|
|
|
# |
231
|
|
|
|
|
|
|
sub shoot { |
232
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
233
|
0
|
|
|
|
|
|
my @path = @_; |
234
|
|
|
|
|
|
|
|
235
|
0
|
|
|
|
|
|
my $cur = $self -> location; |
236
|
|
|
|
|
|
|
|
237
|
0
|
|
|
|
|
|
foreach my $p (@path) { |
238
|
|
|
|
|
|
|
# |
239
|
|
|
|
|
|
|
# Is $p a valid exit of $cur? |
240
|
|
|
|
|
|
|
# |
241
|
0
|
|
|
|
|
|
my $e = $cur -> exit_by_name ($p); |
242
|
0
|
0
|
|
|
|
|
unless ($e) { |
243
|
|
|
|
|
|
|
# |
244
|
|
|
|
|
|
|
# Not a valid exit. Pick one at random. |
245
|
|
|
|
|
|
|
# |
246
|
0
|
|
|
|
|
|
my @e = $cur -> exits; |
247
|
0
|
|
|
|
|
|
$e = $e [rand @e]; |
248
|
|
|
|
|
|
|
} |
249
|
0
|
|
|
|
|
|
$cur = $e; |
250
|
|
|
|
|
|
|
|
251
|
0
|
0
|
|
|
|
|
if ($cur -> has_hazard ($WUMPUS)) {return $WUMPUS} |
|
0
|
|
|
|
|
|
|
252
|
0
|
0
|
|
|
|
|
if ($cur == $self -> location) {return $PLAYER} |
|
0
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
# |
259
|
|
|
|
|
|
|
# Stir the Wumpus. It *may* move. |
260
|
|
|
|
|
|
|
# |
261
|
|
|
|
|
|
|
# Return true if it moves, false otherwise. |
262
|
|
|
|
|
|
|
# |
263
|
|
|
|
|
|
|
sub stir_wumpus { |
264
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
265
|
|
|
|
|
|
|
|
266
|
0
|
0
|
|
|
|
|
if (rand (1) < $WUMPUS_MOVES) { |
267
|
|
|
|
|
|
|
# |
268
|
|
|
|
|
|
|
# He moves. |
269
|
|
|
|
|
|
|
# |
270
|
0
|
|
|
|
|
|
my @exits = $self -> wumpus -> exits; |
271
|
0
|
|
|
|
|
|
my $new = $exits [rand @exits]; |
272
|
|
|
|
|
|
|
|
273
|
0
|
0
|
|
|
|
|
if ($::DEBUG) { |
274
|
0
|
|
|
|
|
|
say STDERR "Wumpus moves to ", $new -> name; |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
|
277
|
0
|
|
|
|
|
|
$self -> wumpus -> clear_hazard ($WUMPUS); |
278
|
0
|
|
|
|
|
|
$new -> set_hazard ($WUMPUS); |
279
|
0
|
|
|
|
|
|
$self -> set_wumpus ($new); |
280
|
0
|
|
|
|
|
|
return 1; |
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
|
283
|
0
|
|
|
|
|
|
return 0; |
284
|
|
|
|
|
|
|
} |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
__END__ |