-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmastermind.rb
More file actions
268 lines (221 loc) · 5.42 KB
/
mastermind.rb
File metadata and controls
268 lines (221 loc) · 5.42 KB
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
require 'pry-byebug'
module GameRules
COLORS = %w[red yellow green blue white black].freeze
def initial_setup
@code = []
@guess = []
@pegs = []
@round = 1
end
def clear
@pegs.clear
@guess.clear
@code_clone.clear
end
def clone_code
@code_clone = @code.clone
end
def red_pegs?
@code.each_index do |idx|
next unless @code[idx] == @guess[idx]
@pegs << 'RED'
@code_clone[idx] = 'x'
@guess[idx] = 'z'
end
end
def white_pegs?
@code_clone.each do |val|
next unless @guess.any?(val)
# If same color is present in code more than once, and the guess contains that
# color in the wrong position but only once, # of white pegs awarded = # of
# times color present in code. To prevent this:
@pegs << 'WHITE'
@guess[@guess.index(val)] = 'z' if @code_clone.count(val) > @guess.count(val)
end
end
def check_pegs
red_pegs?
white_pegs?
end
def display_pegs
puts "\nPegs: #{@pegs.shuffle.join(' ')}\n "
end
def game_won?
@pegs == %w[RED RED RED RED]
end
def game_over?
@round == 13
end
def code_breaker
CodeBreaker.new
end
def code_maker
CodeMaker.new
end
end
class Game
include GameRules
attr_reader :role
def initialize
choose_role
play
end
def play
code_breaker if role == 1
code_maker if role == 2
end
def choose_role
puts 'Enter 1 to be the code-breaker or 2 to be the code-maker.'
@role = gets.chomp.to_i
return if role == 1 || role == 2
puts 'Invalid entry.'
choose_role
end
end
class CodeBreaker
include GameRules
def initialize
initial_setup
generate_code
clone_code
play_rounds
end
def play_rounds
until game_won? || game_over?
clear
solicit_guess
clone_code
check_pegs
display_pegs
puts "You've guessed the code!" if game_won?
@round += 1
if game_over?
puts "Game over. You didn't guess correctly within 12 rounds."
puts "The code was: #{@code.join(' ')}."
end
end
end
def generate_code
4.times { @code << COLORS.sample }
end
def solicit_guess
puts 'Enter your guess:'
puts '(Example: white red green black)' if @round == 1
@guess << gets.chomp.split(' ')
@guess.flatten!
return if valid_guess?
puts 'Invalid guess, please try again.'
@guess.clear
solicit_guess
end
def valid_guess?
@guess.count == 4 && @guess.all? { |color| COLORS.include?(color) }
end
end
class CodeMaker
include GameRules
def initialize
initial_setup
choose_code
valid_code?
generate_possible_codes
reject_numbers
computer_guess
end
def choose_code
puts 'Enter the code that you want the computer to break:'
puts '(Example: white red green black)'
@code << gets.chomp.split(' ')
@code.flatten!
clone_code
@original_code = @code
end
def valid_code?
return if @code.count == 4 && @code.all? { |color| COLORS.include?(color) }
puts 'Invalid code, please try again.'
@code.clear
choose_code
end
def generate_possible_codes
@possible_codes = (1111..6666).to_a
end
def reject_numbers
[7, 8, 9, 0].each do |num_to_delete|
@possible_codes.delete_if { |num| num.to_s.include?(num_to_delete.to_s) }
end
end
def computer_guess
find_first_peg(1, 2)
until game_won? || game_over?
switch_code_to_guess
eliminate_numbers unless game_won?
new_guess unless game_won?
puts "The computer has guessed the code in #{@round - 1} tries." if game_won?
puts "Game over. The computer didn't guess correctly within 12 rounds." if !game_won? && game_over?
end
end
def find_first_peg(num1, num2)
until red_pegs_count >= 1 || white_pegs_count >= 1
initial_guess([num1, num1, num2, num2])
num1 += 2
num2 += 2
find_first_peg(num1, num2)
puts "The computer guessed the code in #{@round - 1} tr#{@round - 1 == 1 ? 'y' : 'ies'}." if game_won?
end
end
def initial_guess(guess)
@pegs.clear
@guess = guess
@guess_clone = @guess.clone
numbers_to_colors(@guess)
sleep(1.5)
puts "Round ##{@round}: The computer guesses: #{@guess.join(' ')}"
check_pegs
display_pegs
@original_red_pegs = red_pegs_count
@original_white_pegs = white_pegs_count
@round += 1
end
def numbers_to_colors(numbers)
numbers.each_with_index do |val, idx|
numbers[idx] = COLORS[val - 1]
end
end
def switch_code_to_guess
@guess = @guess_clone
@code = @guess
numbers_to_colors(@code)
end
def eliminate_numbers
@possible_codes.reverse_each do |possible_code|
# To turn 1111 into array of 1's
@guess = possible_code.to_s.split('').map(&:to_i)
numbers_to_colors(@guess)
@pegs.clear
clone_code
check_pegs
delete_from_set?(possible_code)
@pegs.clear
end
end
def new_guess
@pegs.clear
@code = @original_code
clone_code
new_guess = @possible_codes[0].to_s.split('').map(&:to_i)
initial_guess(new_guess)
end
def delete_from_set?(possible_code)
@possible_codes.delete(possible_code) unless equal_pegs?
end
def equal_pegs?
@original_red_pegs == red_pegs_count && @original_white_pegs == white_pegs_count
end
def red_pegs_count
@pegs.count { |peg| peg == 'RED' }
end
def white_pegs_count
@pegs.count { |peg| peg == 'WHITE' }
end
end
Game.new