-
Notifications
You must be signed in to change notification settings - Fork 0
/
wumpus.rb
203 lines (162 loc) · 5.28 KB
/
wumpus.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#!/usr/bin/env ruby
#
# Hunt the Wumpus
# Metaprogramming Edition
#
# By RC Howe
# http://www.rchowe.com
#
if ENV['HARDMODE']
ChamberCount = 25
elsif ENV['CHAMBER_COUNT']
ChamberCount = ENV['CHAMBER_COUNT'].to_i
else
ChamberCount = 15
end
# The method that starts the game
def let_the_hunt_begin
# The two functions we're going to use
# Dynamically defines a method on Object
def defn symbol, &block
self.class.send :define_method, symbol, &block
end
# Dynamically undefines a method on Object
def undefn symbol
self.class.send :remove_method, symbol
end
def undefn_each arr
arr.each do |j|
begin
undefn j
rescue
end
end
end
# Generates a random symbol
def generate_symbol
sym = (0...4).map{65.+(rand(25)).chr}.join.downcase.to_sym
if Object.methods.include? sym
generate_symbol
else
sym
end
end
# Check if a game's already running
if $game_running
print 'Would you like to quit? [y/n] '
exit if gets.chomp.downcase == 'y'
return
end
# The game is now running
$game_running = true
# Each 'cavern' is a node, represented by an array
descriptions = [:cavernous, :flooded, :smelly, :clean, :grimy, :mucky,
:muddy, :large, :small, :wide, :wet, :stuffy, :overgrown,
:warm, :cold, :red, :greenish, :blueish, :yellow,
:purpleish, :dingy, :chilly, :haunted, :ghostly, :tiny ]
nodes = descriptions.take(ChamberCount).shuffle.map { |x| [x, :safe] }
nodes[0][1] = :wumpus
nodes[1][1] = :pit
# The first node is the one the player will climb down into.
# Therefore, we shuffle until there is nothing dangerous there
# and replace whatever is there with the ladder
nodes.shuffle! while nodes[0][1] != :safe
# Game over
defn :game_over do |str|
puts <<-EOF
\033[1m#{str}\033[0m.
If you would like to play again, call \033[1mlet_the_hunt_begin\033[0m.
EOF
$game_running = false
undefn_each nodes.map { |node| node[0] }
undefn :shoot
nil
end
# Let the user shoot
defn :shoot do |node|
# Check if it's a valid node
ns = nodes.reject { |x| x[0] != node }
# If it's not valid
if ns.nil? or ns.empty?
game_over "You shot the wall!"
# If you shot the wumpus
elsif ns.first[1] == :wumpus
game_over "You \033[32mshot the Wumpus!\033[0m."
# If you didn't shoot the wumpus
else
game_over "You wasted your only bullet."
end
end
# Explain the situation to the user
puts <<-EOF
You are deep in the Caves of Closure, hunting the mysterious Wumpus.
You have a gun (called with \033[1mshoot(cavern_description)\033[0m) with one bullet. Use it well.
You are in a cavern with a ladder, leading to a \033[1m#{nodes[0][0]}\033[0m chamber.
Call the function (by typing it's name) \033[1m#{nodes[0][0]}\033[0m to climb down the ladder
and enter the first cavern.
EOF
# The definer, an object which creates and destroys caverns.
definer = lambda do |index, &block|
# Determine which nodes are adjacent
adjacent_indicies = [(index + ChamberCount - 1) % ChamberCount,
(index + 1) % ChamberCount,
(index + 4) % ChamberCount,
(index + ChamberCount - 4) % ChamberCount].uniq.
shuffle
adjacent_nodes = adjacent_indicies.map { |x| nodes[x] }
adjacents = Hash[ adjacent_indicies.zip adjacent_nodes ]
# Define the given cavern as a function with the node name
defn nodes[index][0] do
# This node
node = nodes[index]
# Call the 'teardown' method of the previous node, if given
block.call unless block.nil?
# Define the new method for each adjacent node
adjacents.each do |i, n|
definer.call i do
undefn_each adjacent_nodes.map { |x| x[0] }
end
end
# Describe the chamber to the user
if [:wumpus, :pit, :ladder].include? node[1]
print "\n You have entered a chamber with a #{node[1]} in it."
else
print "\n You have entered a #{node[0]} chamber."
end
# If the player entered a bad chamber...
case node[1]
when :wumpus
game_over "\033[31mThe wumpus ate you\033[0m"
return
when :pit
game_over "You have fallen into a pit"
return
end
# Look in the adjacent chambers for a wumpus or pit
if adjacent_nodes.inject(false) { |a, n| (a | (n[1] == :wumpus)) }
print " There is \033[31;1mblood on the walls\033[0m."
end
if adjacent_nodes.inject(false) { |a, n| (a | (n[1] == :pit)) }
print " You feel a \033[34;1mbreeze\033[0m."
end
print "\n"
# Inform the user of the tunnels
puts "\n There are tunnels to " +
adjacent_nodes[0..adjacent_nodes.length-2].map { |x| "\033[1m#{x[0]}\033[0m" }.join(', ') +
"#{',' if adjacent_nodes.length > 2} and " + "\033[1m#{adjacent_nodes[adjacent_nodes.length-1][0]}\033[0m chambers.\n\n"
# Return the tunnel ids
adjacent_nodes.map { |n| n[0] }[0..adjacent_nodes.length-1]
end
end
definer.call 0
return nodes[0][0]
end
# Inform the user of the game
print <<-EOF
Hunt the
_ _ _ _ _ _______ _____ _ _ _________
| | | | | | | | |_____] | | |______
|__|__| |_____| | | | | |_____| ______|
Metaprogramming Edition
Welcome to Hunt the Wumpus! Call \033[1mlet_the_hunt_begin\033[0m to start.
EOF