hangman-cli 2.0.2
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/.rspec +1 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +62 -0
- data/bin/bundle +114 -0
- data/bin/byebug +29 -0
- data/bin/hangman +8 -0
- data/bin/htmldiff +29 -0
- data/bin/ldiff +29 -0
- data/bin/rspec +29 -0
- data/bin/rubocop +29 -0
- data/bin/ruby-parse +29 -0
- data/bin/ruby-rewrite +29 -0
- data/lib/answer.rb +16 -0
- data/lib/dictionary.rb +23 -0
- data/lib/display.rb +90 -0
- data/lib/gallows.rb +97 -0
- data/lib/game.rb +132 -0
- data/lib/game_save.rb +28 -0
- data/lib/guess.rb +36 -0
- data/lib/guess_logic.rb +49 -0
- data/lib/hangman.rb +7 -0
- data/lib/player.rb +10 -0
- data/lib/player_name.rb +25 -0
- data/lib/word_to_guess.rb +10 -0
- data/spec/answer_spec.rb +42 -0
- data/spec/dictionary_spec.rb +31 -0
- data/spec/display_spec.rb +234 -0
- data/spec/gallows_spec.rb +119 -0
- data/spec/game_save_spec.rb +35 -0
- data/spec/game_spec.rb +107 -0
- data/spec/guess_logic_spec.rb +138 -0
- data/spec/guess_spec.rb +51 -0
- data/spec/player_name_spec.rb +62 -0
- data/spec/player_spec.rb +11 -0
- data/spec/spec_helper.rb +105 -0
- data/spec/word_to_guess_spec.rb +16 -0
- data/static/test_file.txt +9 -0
- data/static/words.txt +61406 -0
- metadata +82 -0
data/lib/display.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'gallows'
|
4
|
+
|
5
|
+
# Module to display all in game text.
|
6
|
+
module Display
|
7
|
+
def self.welcome_message
|
8
|
+
puts "Welcome to Hangman.\n\n"
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.instruction_intro
|
12
|
+
puts "If you'd like instructions enter 'i'."
|
13
|
+
puts 'Otherwise press return to continue.'
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.instructions
|
17
|
+
puts <<-MSG
|
18
|
+
Reveal the hidden word before your man is hung.
|
19
|
+
|
20
|
+
You may guess a single letter at a time.
|
21
|
+
A correct guess will result in that letter being filled in.
|
22
|
+
Incorrect guesses will result in a part of the stick man being drawn.
|
23
|
+
You have 6 incorrect guesses to play with to save your man.
|
24
|
+
You may also guess the whole word at any time. But be careful; an incorrect guess is game over.
|
25
|
+
|
26
|
+
The game is auto saved after each round.
|
27
|
+
Use the load function at game start to load a previously saved game.
|
28
|
+
Enter 'quit game' at any time to end session.
|
29
|
+
|
30
|
+
MSG
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.enter_name
|
34
|
+
puts 'Enter your name: '
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.play_again
|
38
|
+
puts "Would you like to play again? Enter 'yes' or 'no': "
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.leave
|
42
|
+
puts 'Okay, bye.'
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.validation_errors(errors)
|
46
|
+
puts errors.join(', ') unless errors.empty?
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.thank_player(player)
|
50
|
+
puts "Thanks #{player.name}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.word_to_guess(guess_logic)
|
54
|
+
puts " Word to guess: #{guess_logic.guessed_word}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.gallows(guess_logic)
|
58
|
+
puts Gallows::GALLOWS[guess_logic.incorrect_guesses]
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.beginning_of_guess_round(guess_logic)
|
62
|
+
puts <<~MSG
|
63
|
+
#{gallows(guess_logic)}
|
64
|
+
#{word_to_guess(guess_logic)}
|
65
|
+
Wrong letters: #{guess_logic.incorrect_letters.join(', ')}
|
66
|
+
Incorrect guesses: #{guess_logic.incorrect_guesses}\n\n
|
67
|
+
Make your guess:
|
68
|
+
MSG
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.end_of_guess_round(guess_logic)
|
72
|
+
puts guess_logic.messages.pop.to_s
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.victory(word_to_guess)
|
76
|
+
puts "\n Yes! The word was #{word_to_guess.word.upcase}."
|
77
|
+
puts "\n You win!\n\n"
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.defeat(word_to_guess)
|
81
|
+
puts Gallows.finish
|
82
|
+
puts "\n Sorry, you lose!"
|
83
|
+
puts "\n The word was #{word_to_guess.word.upcase}\n\n"
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.load_game
|
87
|
+
puts "Enter 'load' to load previously saved game."
|
88
|
+
puts 'Or press return to start a new game.'
|
89
|
+
end
|
90
|
+
end
|
data/lib/gallows.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Module to hold Gallow graphics
|
4
|
+
module Gallows
|
5
|
+
def self.start
|
6
|
+
<<-GALLOWS
|
7
|
+
===========
|
8
|
+
|/ |
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|\\
|
13
|
+
============
|
14
|
+
GALLOWS
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.head
|
18
|
+
<<-GALLOWS
|
19
|
+
===========
|
20
|
+
|/ |
|
21
|
+
| O
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|\\
|
25
|
+
============
|
26
|
+
GALLOWS
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.body
|
30
|
+
<<-GALLOWS
|
31
|
+
===========
|
32
|
+
|/ |
|
33
|
+
| O
|
34
|
+
| |
|
35
|
+
|
|
36
|
+
|\\
|
37
|
+
============
|
38
|
+
GALLOWS
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.right_arm
|
42
|
+
<<-GALLOWS
|
43
|
+
===========
|
44
|
+
|/ |
|
45
|
+
| O
|
46
|
+
| /|
|
47
|
+
|
|
48
|
+
|\\
|
49
|
+
============
|
50
|
+
GALLOWS
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.left_arm
|
54
|
+
<<-GALLOWS
|
55
|
+
===========
|
56
|
+
|/ |
|
57
|
+
| O
|
58
|
+
| /|\\
|
59
|
+
|
|
60
|
+
|\\
|
61
|
+
============
|
62
|
+
GALLOWS
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.right_leg
|
66
|
+
<<-GALLOWS
|
67
|
+
===========
|
68
|
+
|/ |
|
69
|
+
| O
|
70
|
+
| /|\\
|
71
|
+
| /
|
72
|
+
|\\
|
73
|
+
============
|
74
|
+
GALLOWS
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.finish
|
78
|
+
<<-GALLOWS
|
79
|
+
===========
|
80
|
+
|/ |
|
81
|
+
| O
|
82
|
+
| /|\\
|
83
|
+
| / \\
|
84
|
+
|\\
|
85
|
+
============
|
86
|
+
GALLOWS
|
87
|
+
end
|
88
|
+
|
89
|
+
GALLOWS = { 0 => start,
|
90
|
+
1 => head,
|
91
|
+
2 => body,
|
92
|
+
3 => right_arm,
|
93
|
+
4 => left_arm,
|
94
|
+
5 => right_leg,
|
95
|
+
6 => finish }
|
96
|
+
.freeze
|
97
|
+
end
|
data/lib/game.rb
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'player'
|
4
|
+
require_relative 'dictionary'
|
5
|
+
require_relative 'word_to_guess'
|
6
|
+
require_relative 'gallows'
|
7
|
+
require_relative 'guess_logic'
|
8
|
+
require_relative 'guess'
|
9
|
+
require_relative 'display'
|
10
|
+
require_relative 'player_name'
|
11
|
+
require_relative 'game_save'
|
12
|
+
require_relative 'answer'
|
13
|
+
require 'yaml'
|
14
|
+
|
15
|
+
# Class to hold all main game elements and behaviour
|
16
|
+
class Game
|
17
|
+
attr_accessor :player, :word_to_guess, :guess_logic
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@player = nil
|
21
|
+
@word_to_guess = word
|
22
|
+
@guess_logic = GuessLogic.new(word_to_guess)
|
23
|
+
end
|
24
|
+
|
25
|
+
def start
|
26
|
+
welcome
|
27
|
+
load_game
|
28
|
+
instructions
|
29
|
+
player_setup
|
30
|
+
main_game_loop
|
31
|
+
end
|
32
|
+
|
33
|
+
def main_game_loop
|
34
|
+
loop do
|
35
|
+
Gallows::GALLOWS[guess_logic.incorrect_guesses]
|
36
|
+
# puts word_to_guess.word TESTING
|
37
|
+
guess
|
38
|
+
victory if win?
|
39
|
+
defeat if game_over?
|
40
|
+
GameSave.save(self)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def player_setup
|
45
|
+
loop do
|
46
|
+
Display.enter_name
|
47
|
+
player_name = PlayerName.new($stdin.gets)
|
48
|
+
Display.validation_errors(player_name.errors)
|
49
|
+
redo unless player_name.valid?
|
50
|
+
self.player = Player.new(player_name.answer)
|
51
|
+
Display.thank_player(player)
|
52
|
+
break
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def guess
|
57
|
+
loop do
|
58
|
+
Display.beginning_of_guess_round(guess_logic)
|
59
|
+
guess = Guess.new($stdin.gets, word_to_guess)
|
60
|
+
Display.validation_errors(guess.errors)
|
61
|
+
redo unless guess.valid?
|
62
|
+
full_word_check(guess) if guess_logic.full_word_guess?(guess)
|
63
|
+
guess_logic.compare(guess)
|
64
|
+
Display.end_of_guess_round(guess_logic)
|
65
|
+
break
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def welcome
|
72
|
+
Display.welcome_message
|
73
|
+
end
|
74
|
+
|
75
|
+
def instructions
|
76
|
+
Display.instruction_intro
|
77
|
+
response = Answer.new($stdin.gets)
|
78
|
+
Display.instructions if response.answer == 'i'
|
79
|
+
end
|
80
|
+
|
81
|
+
def load_game
|
82
|
+
Display.load_game
|
83
|
+
response = Answer.new($stdin.gets)
|
84
|
+
begin
|
85
|
+
GameSave.load.main_game_loop if response.answer == 'load'
|
86
|
+
rescue StandardError
|
87
|
+
puts 'No current save game.'
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def full_word_check(guess)
|
92
|
+
if guess_logic.correct_word?(guess)
|
93
|
+
victory
|
94
|
+
else
|
95
|
+
defeat
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def word
|
100
|
+
dictionary = Dictionary.new('words.txt')
|
101
|
+
WordToGuess.new(dictionary.word)
|
102
|
+
end
|
103
|
+
|
104
|
+
def game_over?
|
105
|
+
guess_logic.incorrect_guesses > 5
|
106
|
+
end
|
107
|
+
|
108
|
+
def win?
|
109
|
+
guess_logic.guessed_word == word_to_guess.word
|
110
|
+
end
|
111
|
+
|
112
|
+
def victory
|
113
|
+
Display.victory(word_to_guess)
|
114
|
+
reload
|
115
|
+
end
|
116
|
+
|
117
|
+
def defeat
|
118
|
+
Display.defeat(word_to_guess)
|
119
|
+
reload
|
120
|
+
end
|
121
|
+
|
122
|
+
def reload
|
123
|
+
Display.play_again
|
124
|
+
answer = $stdin.gets.chomp.downcase
|
125
|
+
if answer == 'yes'
|
126
|
+
Game.new.main_game_loop
|
127
|
+
else
|
128
|
+
Display.leave
|
129
|
+
exit
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
data/lib/game_save.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
# Class to save and load game
|
7
|
+
class GameSave
|
8
|
+
def self.save(game)
|
9
|
+
game_data = YAML.dump(game)
|
10
|
+
save_file = File.join(File.dirname(__FILE__), '../lib/save_games/savegame.txt')
|
11
|
+
dirname = File.dirname(save_file)
|
12
|
+
|
13
|
+
FileUtils.mkdir(dirname) unless File.directory?(dirname)
|
14
|
+
|
15
|
+
File.open(save_file, 'w') do |f|
|
16
|
+
f.write game_data
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.load
|
21
|
+
save_file = File.join(File.dirname(__FILE__), '../lib/save_games/savegame.txt')
|
22
|
+
|
23
|
+
return nil unless File.exist?(save_file)
|
24
|
+
|
25
|
+
game_data = File.open(save_file, 'r', &:read)
|
26
|
+
YAML.load(game_data)
|
27
|
+
end
|
28
|
+
end
|
data/lib/guess.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'answer'
|
4
|
+
|
5
|
+
# Class to model answers that are guesses
|
6
|
+
class Guess < Answer
|
7
|
+
attr_reader :word_to_guess
|
8
|
+
attr_accessor :errors
|
9
|
+
|
10
|
+
def initialize(answer, word_to_guess)
|
11
|
+
super(answer)
|
12
|
+
@word_to_guess = word_to_guess.word
|
13
|
+
valid?
|
14
|
+
end
|
15
|
+
|
16
|
+
def valid?
|
17
|
+
self.errors = []
|
18
|
+
errors << 'Must be a letter' unless letter?
|
19
|
+
errors << 'Must be a single letter or guessing the full word' unless single_letter? || full_word_guess?
|
20
|
+
errors.empty?
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def letter?
|
26
|
+
answer.match?(/[a-z]/)
|
27
|
+
end
|
28
|
+
|
29
|
+
def single_letter?
|
30
|
+
answer.size == 1
|
31
|
+
end
|
32
|
+
|
33
|
+
def full_word_guess?
|
34
|
+
answer.size == word_to_guess.size
|
35
|
+
end
|
36
|
+
end
|
data/lib/guess_logic.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Class for processing guesses
|
4
|
+
class GuessLogic
|
5
|
+
attr_reader :word_to_guess, :guessed_letters
|
6
|
+
attr_accessor :incorrect_guesses, :incorrect_letters,
|
7
|
+
:messages
|
8
|
+
|
9
|
+
def initialize(word_to_guess)
|
10
|
+
@word_to_guess = word_to_guess.word
|
11
|
+
@guessed_letters = []
|
12
|
+
@incorrect_letters = []
|
13
|
+
@incorrect_guesses = 0
|
14
|
+
@messages = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def guessed_word
|
18
|
+
word_to_guess.chars.map { |char| guessed_letters.include?(char) ? char : '-' }.join
|
19
|
+
end
|
20
|
+
|
21
|
+
def compare(guess)
|
22
|
+
if correct_guess?(guess)
|
23
|
+
add_letters(guessed_letters, guess)
|
24
|
+
messages << "\n That's Correct!"
|
25
|
+
else
|
26
|
+
add_letters(incorrect_letters, guess)
|
27
|
+
self.incorrect_guesses += 1
|
28
|
+
messages << "\n Sorry, that's incorrect."
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def full_word_guess?(guess)
|
33
|
+
word_to_guess.length == guess.answer.length
|
34
|
+
end
|
35
|
+
|
36
|
+
def correct_word?(guess)
|
37
|
+
word_to_guess == guess.answer
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def correct_guess?(guess)
|
43
|
+
word_to_guess.include?(guess.answer)
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_letters(collection, guess)
|
47
|
+
collection << guess.answer
|
48
|
+
end
|
49
|
+
end
|
data/lib/hangman.rb
ADDED