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 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
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mastermind.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,6 @@
1
+ guard :rspec, cmd: 'bundle exec rspec --format documentation --fail-fast --color' do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
6
+
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
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/bin/mastermind ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib_dir = File.expand_path('../../lib',__FILE__)
4
+ $LOAD_PATH.unshift(lib_dir)
5
+
6
+ require 'mastermind/cli'
7
+ Mastermind::CLI.new($stdin, $stdout).run
@@ -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