mastermind-nowsiany 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/Gemfile +4 -0
- data/Guardfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +15 -0
- data/Rakefile +2 -0
- data/bin/mastermind +7 -0
- data/lib/mastermind/cli.rb +51 -0
- data/lib/mastermind/game_round.rb +180 -0
- data/lib/mastermind/interact.rb +235 -0
- data/lib/mastermind/play_game.rb +74 -0
- data/lib/mastermind/player.rb +17 -0
- data/lib/mastermind/processor.rb +48 -0
- data/lib/mastermind/version.rb +3 -0
- data/lib/mastermind.rb +28 -0
- data/mastermind.gemspec +27 -0
- data/spec/cli_spec.rb +46 -0
- data/spec/game_round_spec.rb +268 -0
- data/spec/interact_spec.rb +37 -0
- data/spec/play_game_spec.rb +90 -0
- data/spec/processor_spec.rb +63 -0
- data/spec/spec_helper.rb +5 -0
- metadata +143 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: bdc1a3944022d7c135c56e35f371f3b847e80698
|
4
|
+
data.tar.gz: 113b5f27d62d26699bf4f4ecaef3a1aaba3686cf
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 78d0599fdbe3e1d311dd9f37b9a04a2e41c74ea6a2c8fb2feb756571f7fd0457537b77a015abb600e0a9457ff63523ac9cdb2f7c5d6bcb3f33abb04e36f437e9
|
7
|
+
data.tar.gz: 0c5191130ed31efb2e120d640d245e1e486c8e609816805fa7e169a382d25aab37b3448c9ed5c22b401f8c37402a1a54c0c4b56095ad46dece2eb65e093a0d39
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Nathan Owsiany
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# Mastermind
|
2
|
+
|
3
|
+
Mastermind is a command line code breaking game. ([more here](http://en.wikipedia.org/wiki/Mastermind_(board_game)))
|
4
|
+
|
5
|
+
There are both single and multi-player modes. In multi-player mode players create the secret that their opponent guesses.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
To install run:
|
10
|
+
|
11
|
+
$ gem install mastermind
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
TODO: Write usage instructions here
|
data/Rakefile
ADDED
data/bin/mastermind
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'mastermind/interact'
|
2
|
+
|
3
|
+
module Mastermind
|
4
|
+
class CLI
|
5
|
+
attr_accessor :command
|
6
|
+
attr_reader :interact, :instream, :outstream, :game
|
7
|
+
|
8
|
+
def initialize(instream, outstream)
|
9
|
+
@instream = instream
|
10
|
+
@outstream = outstream
|
11
|
+
@command = ""
|
12
|
+
@interact = Mastermind::Interact.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def run
|
16
|
+
outstream.puts interact.screen_clear
|
17
|
+
outstream.puts interact.print_title
|
18
|
+
outstream.puts interact.print_intro
|
19
|
+
until quit?
|
20
|
+
get_command
|
21
|
+
process_command
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_command
|
26
|
+
outstream.print interact.command_prompt
|
27
|
+
self.command = instream.gets.strip.upcase
|
28
|
+
end
|
29
|
+
|
30
|
+
def process_command
|
31
|
+
case
|
32
|
+
when quit? then outstream.puts interact.print_farewell
|
33
|
+
when instructions? then outstream.puts interact.print_instructions
|
34
|
+
when play? then Mastermind::PlayGame.new(instream, outstream, interact).run
|
35
|
+
else outstream.puts interact.print_invalid(command)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def quit?
|
40
|
+
command == 'Q' || command == 'QUIT'
|
41
|
+
end
|
42
|
+
|
43
|
+
def play?
|
44
|
+
command == 'P' || command == 'PLAY'
|
45
|
+
end
|
46
|
+
|
47
|
+
def instructions?
|
48
|
+
command == 'I' || command == 'INSTRUCTIONS'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require 'mastermind/processor'
|
2
|
+
require 'io/console'
|
3
|
+
|
4
|
+
module Mastermind
|
5
|
+
class GameRound
|
6
|
+
attr_accessor :interact,
|
7
|
+
:secret,
|
8
|
+
:valid_colors,
|
9
|
+
:command,
|
10
|
+
:guesses,
|
11
|
+
:round_over,
|
12
|
+
:max_guesses,
|
13
|
+
:instream,
|
14
|
+
:outstream,
|
15
|
+
:interact,
|
16
|
+
:start_time,
|
17
|
+
:players
|
18
|
+
|
19
|
+
def initialize(instream, outstream, interact, players)
|
20
|
+
@instream = instream
|
21
|
+
@outstream = outstream
|
22
|
+
@interact = interact
|
23
|
+
@valid_colors = Mastermind::Processor.colors(6)
|
24
|
+
@max_guesses = 12
|
25
|
+
@command = ""
|
26
|
+
@guesses = []
|
27
|
+
@round_over = false
|
28
|
+
@start_time = Time.now
|
29
|
+
@players = players
|
30
|
+
end
|
31
|
+
|
32
|
+
def play
|
33
|
+
player_reset
|
34
|
+
outstream.puts interact.print_round_intro(color_string)
|
35
|
+
secret_gen
|
36
|
+
set_turn_pos
|
37
|
+
outstream.puts interact.multi_player_div if !single_player?
|
38
|
+
until round_over? || game_round_over?
|
39
|
+
player_turn
|
40
|
+
end
|
41
|
+
@round_over = true
|
42
|
+
end
|
43
|
+
|
44
|
+
def player_reset
|
45
|
+
@players.each do |player|
|
46
|
+
player.secret = ["X", "X", "X", "X"]
|
47
|
+
player.round_over = false
|
48
|
+
player.guesses = []
|
49
|
+
player.start_time = Time.now
|
50
|
+
player.completion_time = nil
|
51
|
+
player.turn_pos = nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def player_turn
|
56
|
+
@players.each do |player|
|
57
|
+
if player.round_over == false && !game_round_over?
|
58
|
+
outstream.print interact.guess_prompt(player)
|
59
|
+
player.command = instream.gets.strip.upcase
|
60
|
+
process_command(player)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def secret_gen
|
66
|
+
if single_player? then @players.first.secret = Mastermind::Processor.secret(4, 6)
|
67
|
+
else
|
68
|
+
@players.shuffle
|
69
|
+
outstream.puts interact.print_get_secret
|
70
|
+
@players.last.secret = get_secret(@players.first)
|
71
|
+
@players.first.secret = get_secret(@players.last)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def get_secret(player)
|
76
|
+
outstream.puts interact.print_player_secret_intro(player.name)
|
77
|
+
code = "yyyy"
|
78
|
+
until valid_guess?(code, player.secret, valid_colors)
|
79
|
+
outstream.print interact.secret_guess_prompt
|
80
|
+
code = instream.noecho(&:gets).strip.upcase
|
81
|
+
outstream.puts
|
82
|
+
end
|
83
|
+
code.chars
|
84
|
+
end
|
85
|
+
|
86
|
+
def set_turn_pos
|
87
|
+
@players[0].turn_pos = 0
|
88
|
+
@players[1].turn_pos = 1 if @players[1]
|
89
|
+
end
|
90
|
+
|
91
|
+
def process_command(player)
|
92
|
+
case
|
93
|
+
when quit?(player) then quit_confirm(player)
|
94
|
+
when !valid_guess?(player.command, player.secret, valid_colors) then outstream.puts interact.print_invalid_guess(player.command)
|
95
|
+
when valid_guess?(player.command, player.secret, valid_colors) then guess(player)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def single_player?
|
100
|
+
@players.length == 1
|
101
|
+
end
|
102
|
+
|
103
|
+
def color_string
|
104
|
+
Mastermind.color_option_string(6)
|
105
|
+
end
|
106
|
+
|
107
|
+
def player_round_over!(player)
|
108
|
+
player.round_over = true
|
109
|
+
outstream.puts interact.print_round_over if round_over?
|
110
|
+
end
|
111
|
+
|
112
|
+
def round_over?
|
113
|
+
!@players.any? {|player| player.round_over == false}
|
114
|
+
end
|
115
|
+
|
116
|
+
def game_round_over?
|
117
|
+
round_over
|
118
|
+
end
|
119
|
+
|
120
|
+
def win!(player)
|
121
|
+
time = Time.now - player.start_time
|
122
|
+
player.completion_time = time
|
123
|
+
outstream.puts interact.print_win(num_guesses(player), time.round, player.secret, player)
|
124
|
+
player_round_over!(player)
|
125
|
+
end
|
126
|
+
|
127
|
+
def correct_guess?(player)
|
128
|
+
player.command.chars == player.secret
|
129
|
+
end
|
130
|
+
|
131
|
+
def guesses_remaining?(player)
|
132
|
+
num_guesses(player) < max_guesses
|
133
|
+
end
|
134
|
+
|
135
|
+
def quit?(player)
|
136
|
+
player.command == "Q" || player.command == "QUIT"
|
137
|
+
end
|
138
|
+
|
139
|
+
def out_of_guesses(player)
|
140
|
+
outstream.puts interact.print_out_of_guesses(num_guesses(player), correct_pos(player), correct_color(player), player.command, max_guesses, player)
|
141
|
+
player_round_over!(player)
|
142
|
+
end
|
143
|
+
|
144
|
+
def guess(player)
|
145
|
+
player.guesses << player.command
|
146
|
+
if correct_guess?(player) then win!(player)
|
147
|
+
elsif !guesses_remaining?(player) then out_of_guesses(player)
|
148
|
+
else outstream.puts interact.print_guess_stats(num_guesses(player), correct_pos(player), correct_color(player), player.command, max_guesses, player)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def num_guesses(player)
|
153
|
+
player.guesses.length
|
154
|
+
end
|
155
|
+
|
156
|
+
def correct_pos(player)
|
157
|
+
Mastermind::Processor.num_correct_pos(player.command, player.secret)
|
158
|
+
end
|
159
|
+
|
160
|
+
def correct_color(player)
|
161
|
+
Mastermind::Processor.num_correct_colors(player.command, player.secret)
|
162
|
+
end
|
163
|
+
|
164
|
+
def valid_guess?(command, secret, valid_colors)
|
165
|
+
Mastermind::Processor.validate(command, secret, valid_colors)
|
166
|
+
end
|
167
|
+
|
168
|
+
def quit_confirm(player)
|
169
|
+
outstream.puts interact.print_are_you_sure
|
170
|
+
outstream.print interact.command_prompt
|
171
|
+
confirmation = instream.gets.strip.upcase
|
172
|
+
case confirmation
|
173
|
+
when "Y", "YES"
|
174
|
+
self.round_over = true
|
175
|
+
player.command = "Q"
|
176
|
+
else player.command = ""
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,235 @@
|
|
1
|
+
require 'mastermind/play_game'
|
2
|
+
require 'colorize'
|
3
|
+
|
4
|
+
module Mastermind
|
5
|
+
class Interact
|
6
|
+
def print_title
|
7
|
+
%q(
|
8
|
+
_/ _/ _/ _/ _/
|
9
|
+
_/_/ _/_/ _/_/_/ _/_/_/ _/_/_/_/ _/_/ _/ _/_/ _/_/_/ _/_/ _/_/_/ _/_/_/
|
10
|
+
_/ _/ _/ _/ _/ _/_/ _/ _/_/_/_/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/
|
11
|
+
_/ _/ _/ _/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/
|
12
|
+
_/ _/ _/_/_/ _/_/_/ _/_/ _/_/_/ _/ _/ _/ _/ _/ _/ _/ _/_/_/
|
13
|
+
).colorize(:green)
|
14
|
+
end
|
15
|
+
|
16
|
+
def line_break
|
17
|
+
"
|
18
|
+
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
19
|
+
".colorize(:green)
|
20
|
+
end
|
21
|
+
|
22
|
+
def blank_line
|
23
|
+
"\n"
|
24
|
+
end
|
25
|
+
|
26
|
+
def print_intro
|
27
|
+
line_break +
|
28
|
+
blank_line +
|
29
|
+
"
|
30
|
+
====================================
|
31
|
+
| M4573RM1ND MA1N M3NV |
|
32
|
+
| ---------------------------------- |
|
33
|
+
| |
|
34
|
+
| (i)NStRUCti0nZ |
|
35
|
+
| (p)1aY the g4M3 |
|
36
|
+
| (q)V1t |
|
37
|
+
| |
|
38
|
+
====================================
|
39
|
+
".colorize(:green) +
|
40
|
+
blank_line
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
def print_game_options
|
45
|
+
line_break +
|
46
|
+
blank_line +
|
47
|
+
"
|
48
|
+
====================================
|
49
|
+
| M4573RM1ND 9AM3 0PTZ |
|
50
|
+
|------------------------------------|
|
51
|
+
| |
|
52
|
+
| (s)1nG1e pl4y0r |
|
53
|
+
| (m)u171 pl4y0r |
|
54
|
+
| (q)V1t |
|
55
|
+
| |
|
56
|
+
====================================
|
57
|
+
".colorize(:green) +
|
58
|
+
blank_line
|
59
|
+
end
|
60
|
+
|
61
|
+
def command_prompt
|
62
|
+
"Enter command: "
|
63
|
+
end
|
64
|
+
|
65
|
+
def screen_clear
|
66
|
+
"\e[H\e[2J"
|
67
|
+
end
|
68
|
+
|
69
|
+
def multi_player_div
|
70
|
+
"\n"+
|
71
|
+
"Player 1 Player 2\n"+
|
72
|
+
"==================================================================================="
|
73
|
+
end
|
74
|
+
|
75
|
+
def guess_prompt(player)
|
76
|
+
if player.turn_pos == 0
|
77
|
+
"\n"+
|
78
|
+
"#{player.name} - Enter your guess: "
|
79
|
+
else
|
80
|
+
"\n"+
|
81
|
+
" #{player.name} - Enter your guess: "
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def print_invalid(input)
|
86
|
+
"
|
87
|
+
'#{input}' is not a valid command, please enter a valid command.
|
88
|
+
"
|
89
|
+
end
|
90
|
+
|
91
|
+
def print_out_of_guesses(number_guesses, correct_pos, correct_color, guess, max_guesses, player)
|
92
|
+
if player.turn_pos == 0
|
93
|
+
"\n"+
|
94
|
+
"----------------------------------------\n"+
|
95
|
+
"Guess ##{number_guesses} (#{color_guess(guess)}): #{max_guesses - number_guesses} guesses remaining\n"+
|
96
|
+
"Correct position: #{correct_pos} Correct colors: #{correct_color}\n"+
|
97
|
+
"\n"+
|
98
|
+
"Nope... sorry #{player.name}, that was your last\n"+
|
99
|
+
"guess and you still didn't get it right....\n"+
|
100
|
+
"The secret code was: #{color_guess(player.secret)}\n"+
|
101
|
+
"----------------------------------------\n"
|
102
|
+
else
|
103
|
+
"\n"+
|
104
|
+
" ----------------------------------------\n"+
|
105
|
+
" Guess ##{number_guesses} (#{color_guess(guess)}): #{max_guesses - number_guesses} guesses remaining\n"+
|
106
|
+
" Correct position: #{correct_pos} Correct colors: #{correct_color}\n"+
|
107
|
+
"\n"+
|
108
|
+
" Nope... sorry #{player.name}, that was your last\n"+
|
109
|
+
" guess and you still didn't get it right....\n"+
|
110
|
+
" The secret code was: #{color_guess(player.secret)}\n"+
|
111
|
+
" ----------------------------------------\n"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def print_farewell
|
116
|
+
"
|
117
|
+
Thanks for playing
|
118
|
+
"
|
119
|
+
end
|
120
|
+
|
121
|
+
def print_instructions
|
122
|
+
"
|
123
|
+
These are the instructions.
|
124
|
+
"
|
125
|
+
end
|
126
|
+
|
127
|
+
def print_game_info
|
128
|
+
""
|
129
|
+
end
|
130
|
+
|
131
|
+
def print_player_intro
|
132
|
+
"Player(s) need to enter their name."
|
133
|
+
end
|
134
|
+
|
135
|
+
def name_prompt(player_no)
|
136
|
+
"Player #{player_no} Name: "
|
137
|
+
end
|
138
|
+
|
139
|
+
def print_player_secret_intro(player_name)
|
140
|
+
"
|
141
|
+
#{player_name} - Pick the secret code for your opponent"
|
142
|
+
end
|
143
|
+
|
144
|
+
def secret_guess_prompt
|
145
|
+
"Enter secret code: "
|
146
|
+
end
|
147
|
+
|
148
|
+
def print_get_secret
|
149
|
+
"Time to create some secrets. Each player will create the secret
|
150
|
+
That their opponent will be guessing."
|
151
|
+
end
|
152
|
+
|
153
|
+
def print_round_intro(colors)
|
154
|
+
"
|
155
|
+
A random code consisting of 4 colors has been generated for you.
|
156
|
+
|
157
|
+
The valid color options are:
|
158
|
+
#{colors}
|
159
|
+
|
160
|
+
Enter your guess in the form of '#{color_guess("RGBY")}':
|
161
|
+
------------------------------
|
162
|
+
"
|
163
|
+
end
|
164
|
+
|
165
|
+
def print_round_over
|
166
|
+
"
|
167
|
+
**************************************
|
168
|
+
* Round over *
|
169
|
+
**************************************
|
170
|
+
"
|
171
|
+
end
|
172
|
+
|
173
|
+
def print_are_you_sure
|
174
|
+
"
|
175
|
+
Are you sure you want to quit? (y)es/(n)o".colorize(:red)
|
176
|
+
end
|
177
|
+
|
178
|
+
def print_invalid_guess(invalid_command)
|
179
|
+
"'#{invalid_command}' is not a valid guess, please guess again."
|
180
|
+
end
|
181
|
+
|
182
|
+
def print_guess_stats(number_guesses, correct_pos, correct_color, guess, max_guesses, player)
|
183
|
+
if player.turn_pos == 0
|
184
|
+
"\n"+
|
185
|
+
"----------------------------------------\n"+
|
186
|
+
"Guess ##{number_guesses} (#{color_guess(guess)}): #{max_guesses - number_guesses} guesses remaining\n"+
|
187
|
+
"Correct position: #{correct_pos} Correct colors: #{correct_color}\n"+
|
188
|
+
"\n"+
|
189
|
+
"Nope... sorry #{player.name} let's try that again\n"+
|
190
|
+
"----------------------------------------\n"
|
191
|
+
else
|
192
|
+
"\n"+
|
193
|
+
" ----------------------------------------\n"+
|
194
|
+
" Guess ##{number_guesses} (#{color_guess(guess)}): #{max_guesses - number_guesses} guesses remaining\n"+
|
195
|
+
" Correct position: #{correct_pos} Correct colors: #{correct_color}\n"+
|
196
|
+
"\n"+
|
197
|
+
" Nope... sorry #{player.name} let's try that again\n"+
|
198
|
+
" ----------------------------------------\n"
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def print_win(number_guesses, time, secret, player)
|
203
|
+
if player.turn_pos == 0
|
204
|
+
"\n"+
|
205
|
+
"******************************\n"+
|
206
|
+
" CONGRATULATIONS\n"+
|
207
|
+
"******************************\n"+
|
208
|
+
" You guessed the code (#{color_guess(secret)})\n"+
|
209
|
+
" It took you #{number_guesses} guesses\n"+
|
210
|
+
" over #{time} seconds!\n"+
|
211
|
+
"******************************\n"
|
212
|
+
else
|
213
|
+
"\n"+
|
214
|
+
" ******************************\n"+
|
215
|
+
" CONGRATULATIONS\n"+
|
216
|
+
" ******************************\n"+
|
217
|
+
" You guessed the code (#{color_guess(secret)})\n"+
|
218
|
+
" It took you #{number_guesses} guesses\n"+
|
219
|
+
" over #{time} seconds!\n"+
|
220
|
+
" ******************************\n"
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def color_guess(code)
|
225
|
+
input = code.kind_of?(Array) ? code : code.chars
|
226
|
+
colored = []
|
227
|
+
input.map { |char| colored << char.colorize(color_code(char)) }
|
228
|
+
colored.join("")
|
229
|
+
end
|
230
|
+
|
231
|
+
def color_code(letter)
|
232
|
+
Mastermind::COLOR_CODES[letter]
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'mastermind/interact'
|
2
|
+
require 'mastermind/game_round'
|
3
|
+
require 'mastermind/player'
|
4
|
+
|
5
|
+
module Mastermind
|
6
|
+
class PlayGame
|
7
|
+
attr_accessor :interact, :instream, :outstream, :command
|
8
|
+
|
9
|
+
def initialize(instream, outstream, interact)
|
10
|
+
@instream = instream
|
11
|
+
@outstream = outstream
|
12
|
+
@interact = interact
|
13
|
+
@command = ""
|
14
|
+
end
|
15
|
+
|
16
|
+
def run
|
17
|
+
outstream.puts interact.print_game_info
|
18
|
+
outstream.puts interact.print_game_options
|
19
|
+
until quit?
|
20
|
+
get_command
|
21
|
+
process_command
|
22
|
+
end
|
23
|
+
outstream.puts interact.print_intro
|
24
|
+
end
|
25
|
+
|
26
|
+
def get_command
|
27
|
+
outstream.print interact.command_prompt
|
28
|
+
self.command = instream.gets.strip.upcase
|
29
|
+
end
|
30
|
+
|
31
|
+
def process_command
|
32
|
+
case
|
33
|
+
when command == "S"
|
34
|
+
players = player_gen(1)
|
35
|
+
Mastermind::GameRound.new(instream, outstream, interact, players).play
|
36
|
+
self.command = "Q"
|
37
|
+
when command == "M"
|
38
|
+
players = player_gen(2)
|
39
|
+
Mastermind::GameRound.new(instream, outstream, interact, players).play
|
40
|
+
self.command = "Q"
|
41
|
+
when command == "I"
|
42
|
+
puts "play-game"
|
43
|
+
else outstream.puts interact.print_invalid(command)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def player_gen(num_players)
|
48
|
+
players = []
|
49
|
+
outstream.puts interact.print_player_intro
|
50
|
+
num_players.times do |i|
|
51
|
+
name = get_name(i+1)
|
52
|
+
players << Player.new(name)
|
53
|
+
end
|
54
|
+
players
|
55
|
+
end
|
56
|
+
|
57
|
+
def get_name(player_no)
|
58
|
+
name = ""
|
59
|
+
until valid_name?(name)
|
60
|
+
outstream.print interact.name_prompt(player_no)
|
61
|
+
name = instream.gets.strip
|
62
|
+
end
|
63
|
+
name
|
64
|
+
end
|
65
|
+
|
66
|
+
def valid_name?(name)
|
67
|
+
name.length > 0 && name.length < 12
|
68
|
+
end
|
69
|
+
|
70
|
+
def quit?
|
71
|
+
command == "Q" || command == "QUIT"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Mastermind
|
2
|
+
class Player
|
3
|
+
attr_reader :name
|
4
|
+
attr_accessor :secret, :command, :guesses, :round_over, :start_time, :completion_time, :turn_pos
|
5
|
+
|
6
|
+
def initialize(name)
|
7
|
+
@secret = ["X", "X", "X", "X"]
|
8
|
+
@name = name
|
9
|
+
@round_over = false
|
10
|
+
@guesses = []
|
11
|
+
@command = ""
|
12
|
+
@start_time = nil
|
13
|
+
@completion_time = nil
|
14
|
+
@turn_pos = nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'mastermind'
|
2
|
+
require 'pry'
|
3
|
+
|
4
|
+
module Mastermind
|
5
|
+
class Processor
|
6
|
+
def self.secret(length, num_colors)
|
7
|
+
secret = []
|
8
|
+
length.times do
|
9
|
+
secret << colors(num_colors).sample
|
10
|
+
end
|
11
|
+
secret
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.colors(num_colors)
|
15
|
+
Mastermind::COLORS.first(num_colors)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.validate(guess, code, valid_colors)
|
19
|
+
valid_length?(guess, code) && valid_colors?(guess, valid_colors)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.valid_length?(guess, code)
|
23
|
+
guess.length == code.length
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.valid_colors?(guess, valid_colors)
|
27
|
+
validity = guess.split("").map do |char|
|
28
|
+
valid_colors.any? { |color| color == char }
|
29
|
+
end
|
30
|
+
!validity.any? { |char| char == false }
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.num_correct_colors(guess, code)
|
34
|
+
non_posd_guess = guess.chars.select.with_index { |letter, i| letter != code[i] }
|
35
|
+
non_posd_code = code.select.with_index { |letter, i| letter != guess.chars[i] }
|
36
|
+
colors = []
|
37
|
+
non_posd_guess.map do |letter|
|
38
|
+
idx = non_posd_code.find_index { |char| letter == char }
|
39
|
+
colors << non_posd_code.delete_at(idx) if idx
|
40
|
+
end
|
41
|
+
colors.length
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.num_correct_pos(guess, code)
|
45
|
+
guess.chars.zip(code).count { |comp| comp[0] == comp[1] }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|