-
Notifications
You must be signed in to change notification settings - Fork 1
/
mastermind.rb
357 lines (301 loc) · 8.21 KB
/
mastermind.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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
class Mastermind
LETTERS = ["R", "B", "Y", "G", "O", "P"]
attr_accessor :guess
def initialize
@mode = 0
@guess = []
@hint = []
@code = []
@human_player = HumanPlayer.new
@computer_player = ComputerPlayer.new
end
# This method allows the user to choose the mode they want to play
# they can either be the code breaker or code maker
def set_up_game
system 'clear'
@human_player.get_name
choose_mode
if @mode == 1
play_as_codebreaker
elsif @mode == 2
play_as_codemaker
end
end
# This method makes shows the mode options and makes sure that the mode that the user
# chooses is valid.
def choose_mode
valid = false
while valid == false
puts "Hi #{@human_player.name} do you want to be the code maker or"
puts "the code breaker?"
puts "1. for code breaker"
puts "2. for code maker"
puts
print "Please choose an option: "
@mode = gets.chomp.to_i
if @mode > 0 && @mode <= 2
valid = true
else
system 'clear'
puts "Invalid selection, please choose either option 1 or 2."
end
end
end
# This method will be run if the user chooses the code maker mode.
# it asks the user to enter a 4 letter code and makes sure it is valid
# the computer then makes a random intial guess from the letters array
# the results of this guess will be shown
# the computer then takes the hints into consideration for its next guess
# if the hints contain any exacts, the computer will keep these
# if the the hints contain any nears, they will keep them but put them in
# another random posistion.
# if they have any nopes they wont use them again
# this will repeat until the computer has guessed the code directly
# or if the iteration runs out.
def play_as_codemaker
system 'clear'
puts "Letters to choose from: #{LETTERS.inspect}"
print "Please enter your guess for the four letter code: \n"
@code = @human_player.enter_code
puts
i = 1
while i < 11
if i == 1
@guess = @computer_player.intial_guess
else
@guess = @computer_player.final_array
@computer_player.temp = []
end
puts "Guess Number: #{i}"
get_results
puts "The computer player is making his guess #{i}: "
3.times do
print (".")
sleep(1)
end
show_result
@computer_player.logic(@hint, @guess)
@computer_player.any_nil?
@computer_player.final_array
if win?
puts "The computer has won!"
play_again?
end
i += 1
end
puts "You have won!"
play_again?
end
# This method detemines the hints from the guess array and
# fills the hints array with the following:
# The hint will be exact if the guess is the same letter as the code and in the same position
# the hint will be near if the guess is just the same letter but not in the same position as the code
# the hint will be nope if the guess letter is not in the code
def get_results
i = 0
while i < @guess.length
if @guess[i] == @code[i]
@hint[i] = "Exact"
elsif @code.include?(@guess[i])
@hint[i] = "Near"
else
@hint[i] = "Nope"
end
i += 1
end
return @hint
end
# This method outputs the results of the guess to the screen
# It shows the guess and the coresponding hints below it
def show_result
puts "\n"
puts "\t Choice 1 \t Choice 2\t Choice 3\t Choice 4"
puts "-" * 70
puts "Guess |\t#{@guess[0]}\t #{@guess[1]}\t\t #{@guess[2]}\t\t #{@guess[3]}"
puts "Result |\t#{@hint[0]} \t #{@hint[1]} \t #{@hint[2]} \t #{@hint[3]}"
puts "\n"
end
# This method runs when the user chooses the code breaker mode
# It first gets the computer to generate a random code from the letters array
# it then starts a loop, each time it asks for the user to enter thier guess
# and it will output the results to the screen.
# it will stop when the user guesses the code correctly or when the iteration reaches 10
def play_as_codebreaker
system 'clear'
i = 1
@code = @computer_player.generate_code
while i < 11
puts "\n"
puts "Letters to choose from: #{LETTERS}"
puts "\n"
print "Please enter your guess for the four letter code: \n"
@guess = @human_player.enter_guess
get_results
puts "\n"
puts "Guess Number: #{i}"
show_result
if win?
puts "You have Won!"
play_again?
end
i += 1
end
puts "You have lost!"
play_again?
end
# This method checks to see if all the elements in the hint array
# equal 'Exact' when they do, the method will return true
def win?
@hint.all? {|a| a == "Exact"}
end
# This gives the user the option to play again by going by to
# the set up game method. It also checks to ensure the user has
# entered a valid option
def play_again?
again = false
while again == false
print "Do you want to play again? (y/n): "
input = gets.chomp.downcase
if input == "y"
again = true
load './mastermind.rb'
system 'clear'
set_up_game
elsif input == "n"
again = true
exit
else
puts "Invalid input, Please enter either 'y' or 'n'."
end
end
end
class HumanPlayer
attr_accessor :name
def initialize
@name = nil
end
def get_name
print "What is your name: "
@name = gets.chomp
end
def enter_guess
guess = []
i = 0
while i < 4
print "Letter #{i + 1}: "
guess[i] = gets.chomp.upcase
if LETTERS.include?(guess[i])
i += 1
else
puts "Invalid letter please choose again"
end
end
return guess
end
def enter_code
code = []
i = 0
while i < 4
print "Letter #{i + 1}: "
code[i] = gets.chomp.upcase
if LETTERS.include?(code[i])
i += 1
else
puts "Invalid letter please choose again"
end
end
return code
end
end
class ComputerPlayer
attr_accessor :final_array, :temp, :possible_letters
def initialize
@temp = Array.new(4)
@intial_guess = []
@free_indexes = []
@final_array = []
@possible_letters = LETTERS
end
# This method generates a random code from the letters array
def generate_code
LETTERS.sample(4)
end
# This method makes the computers intial guess, 4 random letters from the letters array
def intial_guess
while @intial_guess.size < 4
@intial_guess << LETTERS[rand(LETTERS.size)]
@intial_guess.uniq!
end
return @intial_guess
end
# This method contains the logic for the computers guesses
# It first checks if any of the hints returned are "Exact"
# if there any, it will add these to a temp array and then
# check for any hints that are equal to "near" if there are
# any it will keep them and put them in a different position
# in the temp array. It will then check if there are any
# "nope" hints. If there are any it will delete them from the
# array of possible letters. The final array is then made from
# the temp array and is returned. This will be used as the
# computers next guess
def logic(hint, guess)
i = 0
while i < hint.size
if hint[i] == "Exact"
@temp[i] = guess[i]
@possible_letters.delete(guess[i])
elsif hint[i] == "Near"
if @temp.count(guess[i]) > 1 || @final_array.count(guess[1]) > 1
find_empty_indexes
@temp[@new_index] = choose_random_letter(guess[i])
else
find_empty_indexes
@temp[@new_index] = guess[i]
end
elsif hint[i] == "Nope"
@possible_letters.delete(guess[i])
@temp[i] = @possible_letters[rand(@possible_letters.size)]
end
i += 1
end
@final_array = @temp
return @final_array
end
# This method checks to see if any elements in the temp array are equal to nil
# if they are, they will be assigned a new random letter.
def any_nil?
i = 0
while i < 4
if @temp[i] == nil
@temp[i] = @possible_letters[rand(@possible_letters.length)]
end
i += 1
end
@final_array = @temp
return @final_array
end
# This method chooses a random letter from the possible_letters array
def choose_random_letter(letter)
new_letter = letter
while new_letter == letter
new_letter = @possible_letters[rand(@possible_letters.size)]
end
return new_letter
end
# This method finds any empty indexes in the temp array
def find_empty_indexes
i = 0
@free_indexes.clear
while i < 4
if @temp[i] == nil
@free_indexes << i
end
i += 1
end
@new_index = @free_indexes[rand(@free_indexes.size)]
return @new_index
end
end
end
mastermind = Mastermind.new
mastermind.set_up_game