codebreaker_vk 0.3.1 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.fasterer.yml +0 -0
- data/.gitignore +0 -0
- data/.overcommit.yml +1 -1
- data/.rspec +0 -0
- data/.rubocop.yml +0 -0
- data/.ruby-gemset +0 -0
- data/.travis.yml +0 -0
- data/CODE_OF_CONDUCT.md +0 -0
- data/Gemfile +0 -0
- data/Gemfile.lock +1 -1
- data/LICENSE.txt +0 -0
- data/README.md +0 -0
- data/Rakefile +0 -0
- data/autoload.rb +9 -9
- data/codebreaker_vk-0.1.0.gem +0 -0
- data/codebreaker_vk-0.2.1.gem +0 -0
- data/codebreaker_vk-0.2.2.gem +0 -0
- data/codebreaker_vk-0.2.3.gem +0 -0
- data/config/i18n.rb +0 -0
- data/config/locales/en.yml +37 -40
- data/database.yaml +8 -0
- data/lib/codebreaker_vk/database.rb +36 -0
- data/lib/codebreaker_vk/game.rb +49 -56
- data/lib/codebreaker_vk/game_console.rb +54 -0
- data/lib/codebreaker_vk/game_menu.rb +76 -0
- data/lib/codebreaker_vk/output.rb +17 -0
- data/lib/codebreaker_vk/table_data.rb +26 -0
- data/lib/codebreaker_vk/validation.rb +12 -0
- data/lib/codebreaker_vk/version.rb +1 -1
- data/main.rb +5 -0
- metadata +10 -11
- data/lib/codebreaker_vk/check_errors.rb +0 -16
- data/lib/codebreaker_vk/console.rb +0 -111
- data/lib/codebreaker_vk/difficulty.rb +0 -38
- data/lib/codebreaker_vk/game_messages.rb +0 -79
- data/lib/codebreaker_vk/game_process.rb +0 -37
- data/lib/codebreaker_vk/game_user.rb +0 -18
- data/lib/codebreaker_vk/uploader/uploader.rb +0 -18
- data/lib/codebreaker_vk/validate/validate.rb +0 -21
- data/lib/codebreaker_vk/validating_data.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ce6a637c137f2f28c5b4abb77c15dea5b45e9fc642a33a9f7e9cf4e811962df5
|
4
|
+
data.tar.gz: e0f628cce3aa5f7d64ec3f65e8971c5890e573fcb1e3c212bb02e544c74c789c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 03ed7c6c1f1e7030416eeed769a1f36c30766b09246549095f89d53963c7a616dcfb52dfc0812aec5e023516f11c68e694b6eba41051a2ebb595a9ef7a2836c1
|
7
|
+
data.tar.gz: 124cd40776dfe7f387794d4eadb5b24a5072da6b57c73a83e8010cd5668798ed4ebb2d09ded52bcb161ca23db7788fd8aa80d5f5ea8d8935df9901aa248bca6f
|
data/.fasterer.yml
CHANGED
File without changes
|
data/.gitignore
CHANGED
File without changes
|
data/.overcommit.yml
CHANGED
data/.rspec
CHANGED
File without changes
|
data/.rubocop.yml
CHANGED
File without changes
|
data/.ruby-gemset
CHANGED
File without changes
|
data/.travis.yml
CHANGED
File without changes
|
data/CODE_OF_CONDUCT.md
CHANGED
File without changes
|
data/Gemfile
CHANGED
File without changes
|
data/Gemfile.lock
CHANGED
data/LICENSE.txt
CHANGED
File without changes
|
data/README.md
CHANGED
File without changes
|
data/Rakefile
CHANGED
File without changes
|
data/autoload.rb
CHANGED
@@ -3,14 +3,14 @@
|
|
3
3
|
require 'i18n'
|
4
4
|
require 'yaml'
|
5
5
|
require_relative 'config/i18n'
|
6
|
-
|
7
|
-
require_relative 'lib/codebreaker_vk/
|
8
|
-
require_relative 'lib/codebreaker_vk/
|
9
|
-
require_relative 'lib/codebreaker_vk/
|
10
|
-
require_relative 'lib/codebreaker_vk/difficulty'
|
6
|
+
|
7
|
+
require_relative 'lib/codebreaker_vk/validation'
|
8
|
+
require_relative 'lib/codebreaker_vk/output'
|
9
|
+
require_relative 'lib/codebreaker_vk/database'
|
11
10
|
require_relative 'lib/codebreaker_vk/game'
|
12
|
-
require_relative 'lib/codebreaker_vk/
|
13
|
-
require_relative 'lib/codebreaker_vk/
|
14
|
-
require_relative 'lib/codebreaker_vk/
|
15
|
-
|
11
|
+
require_relative 'lib/codebreaker_vk/game_console'
|
12
|
+
require_relative 'lib/codebreaker_vk/game_menu'
|
13
|
+
require_relative 'lib/codebreaker_vk/table_data'
|
14
|
+
|
15
|
+
|
16
16
|
require_relative 'lib/codebreaker_vk/version'
|
data/codebreaker_vk-0.1.0.gem
CHANGED
File without changes
|
data/codebreaker_vk-0.2.1.gem
CHANGED
File without changes
|
data/codebreaker_vk-0.2.2.gem
CHANGED
File without changes
|
data/codebreaker_vk-0.2.3.gem
CHANGED
File without changes
|
data/config/i18n.rb
CHANGED
File without changes
|
data/config/locales/en.yml
CHANGED
@@ -1,41 +1,38 @@
|
|
1
1
|
en:
|
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
|
-
cover_error: 'Improper size'
|
40
|
-
include_error: 'Not include in propose inputs'
|
41
|
-
size_error: 'Invalid size'
|
2
|
+
greeting: "Welcome to codebreaker!\n"
|
3
|
+
goodbye: "Goodbye!\n"
|
4
|
+
rules: "GAME RULES\n
|
5
|
+
# Codebreaker is a logic game in which a code-breaker tries to break a secret code created by a code-maker. The\n codemaker, which will be played by the application we’re going to write, creates a secret code of four numbers between\n 1 and 6.\n
|
6
|
+
# The codebreaker gets some number of chances to break the code (depends on chosen difficulty). In each turn, the\n codebreaker makes a guess of 4 numbers. The codemaker then marks the guess with up to 4 signs - + or - or empty\n spaces.\n
|
7
|
+
# A + indicates an exact match: one of the numbers in the guess is the same as one of the numbers in the secret code\n and in the same position. For example:\n
|
8
|
+
Secret number - 1234\n
|
9
|
+
Input number - 6264\n
|
10
|
+
Number of pluses - 2 (second and fourth position)\n
|
11
|
+
# A - indicates a number match: one of the numbers in the guess is the same as one of the numbers in the secret code\n but in a different position. For example:\n
|
12
|
+
Secret number - 1234\n
|
13
|
+
Input number - 6462\n
|
14
|
+
Number of minuses - 2 (second and fourth position)\n
|
15
|
+
# An empty space indicates that there is not a current digit in a secret number.\n
|
16
|
+
# If codebreaker inputs the exact number as a secret number - codebreaker wins the game. If all attempts are spent -\n codebreaker loses.\n
|
17
|
+
# Codebreaker also has some number of hints(depends on chosen difficulty). If a user takes a hint - he receives back a\n
|
18
|
+
separate digit of the secret code.\n"
|
19
|
+
menu: "Choose one of next scenarios: 'start', 'rules', 'stats', 'exit'"
|
20
|
+
choose_difficulty: "Choose difficulty: easy, medium, hell"
|
21
|
+
choose_name: "Choose your name"
|
22
|
+
win: "Congratulations! You win!"
|
23
|
+
lose: "Sorry, you lose. Maybe another time."
|
24
|
+
wrong_difficulty: "There is no such difficulty. Please choose one from listed"
|
25
|
+
wrong_input: "wrong input"
|
26
|
+
wrong_name: "Choosen name is invalid. Name must be between 3 and 20 characters.\nPlease choose another one"
|
27
|
+
wrong_process: "There is no such command or your number is invalid. You can try use hint or exit"
|
28
|
+
wrong_run: "You have passed unexpected command. Please choose one from listed commands"
|
29
|
+
easy: "easy"
|
30
|
+
medium: "medium"
|
31
|
+
hell: "hell"
|
32
|
+
no_hints: "You have no hints"
|
33
|
+
game_process: "%{attempts} attempts and %{hints} hints left. Guess a secret code"
|
34
|
+
secret: "The secret code was %{secret}"
|
35
|
+
save: "Print 'save' if you want to save your result: "
|
36
|
+
game_over: 'Game Over'
|
37
|
+
stats: "Player %{name} played with difficulty level %{difficulty} (%{attempts_total} attempts, %{hints_total} hints) and won with %{attempts_used} attempts and %{hints_used} hints used."
|
38
|
+
no_stats: "Sorry, you have not any saves yet."
|
data/database.yaml
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CodebreakerVk
|
4
|
+
module Database
|
5
|
+
SEED = 'database.yaml'
|
6
|
+
|
7
|
+
def load(path = SEED)
|
8
|
+
YAML.load_file(path)
|
9
|
+
end
|
10
|
+
|
11
|
+
def save(summary, path = SEED)
|
12
|
+
row = TableData.new(summary)
|
13
|
+
if File.exist?(path)
|
14
|
+
table = load(path)
|
15
|
+
table << row
|
16
|
+
File.write(path, table.to_yaml)
|
17
|
+
else
|
18
|
+
File.write(path, [row].to_yaml)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def save_results
|
23
|
+
attempts_total = Game::DIFFICULTY_LEVEL[@game.difficulty][:attempts]
|
24
|
+
hints_total = Game::DIFFICULTY_LEVEL[@game.difficulty][:hints]
|
25
|
+
summary = {
|
26
|
+
name: @game.name,
|
27
|
+
difficulty: @game.difficulty,
|
28
|
+
attempts_total: attempts_total,
|
29
|
+
attempts_used: attempts_total - @game.attempts,
|
30
|
+
hints_total: hints_total,
|
31
|
+
hints_used: hints_total - @game.hints
|
32
|
+
}
|
33
|
+
save(summary)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/codebreaker_vk/game.rb
CHANGED
@@ -2,81 +2,74 @@
|
|
2
2
|
|
3
3
|
module CodebreakerVk
|
4
4
|
class Game
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
@
|
5
|
+
include Output
|
6
|
+
SECRET_CODE_LENGTH = 4
|
7
|
+
RANGE_START = 1
|
8
|
+
RANGE_END = 6
|
9
|
+
NOT_YET = '-'
|
10
|
+
GOT_IT = '+'
|
11
|
+
DIFFICULTY_LEVEL = {
|
12
|
+
easy: { attempts: 15, hints: 3 },
|
13
|
+
medium: { attempts: 10, hints: 2 },
|
14
|
+
hell: { attempts: 5, hints: 1 }
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
attr_accessor :attempts_total, :attempts, :difficulty, :hints_total, :hints, :name, :secret
|
18
|
+
|
19
|
+
def initialize(name:, difficulty:)
|
20
|
+
@name = name
|
21
|
+
@difficulty = difficulty
|
22
|
+
@attempts = DIFFICULTY_LEVEL[difficulty][:attempts]
|
23
|
+
@hints = DIFFICULTY_LEVEL[difficulty][:hints]
|
24
|
+
@secret = make_number
|
25
|
+
@unused_hints = @secret.chars
|
21
26
|
end
|
22
27
|
|
23
|
-
def
|
24
|
-
|
25
|
-
@game_numbers = { code: @breaker_numbers.clone, input: user_input }
|
26
|
-
collect_place_guess + collect_presence_guess
|
28
|
+
def make_number(numbers = RANGE_END)
|
29
|
+
(1..SECRET_CODE_LENGTH).map { rand(RANGE_START..numbers) }.join
|
27
30
|
end
|
28
31
|
|
29
|
-
def
|
30
|
-
|
31
|
-
|
32
|
-
@hints -= 1
|
33
|
-
@showed_hints << @breaker_numbers_copy.pop
|
34
|
-
@showed_hints.last
|
32
|
+
def check(number)
|
33
|
+
@attempts -= 1
|
34
|
+
@last_result = check_numbers(@secret.chars, number.chars)
|
35
35
|
end
|
36
36
|
|
37
|
-
def win?
|
38
|
-
@
|
37
|
+
def win?
|
38
|
+
@last_result == GOT_IT * SECRET_CODE_LENGTH
|
39
39
|
end
|
40
40
|
|
41
|
-
def
|
42
|
-
|
43
|
-
end
|
41
|
+
def use_hint
|
42
|
+
return I18n.t(:no_hints) unless @hints.positive?
|
44
43
|
|
45
|
-
|
46
|
-
|
47
|
-
player_name: @player_name,
|
48
|
-
level: @difficulty[:level],
|
49
|
-
all_hints: @difficulty[:hints],
|
50
|
-
all_attempts: @difficulty[:attempts],
|
51
|
-
left_hints: @hints,
|
52
|
-
left_attempts: @attempts,
|
53
|
-
date: Time.now
|
54
|
-
}
|
44
|
+
@hints -= 1
|
45
|
+
hint(@unused_hints)
|
55
46
|
end
|
56
47
|
|
57
48
|
private
|
58
49
|
|
59
|
-
def
|
60
|
-
|
61
|
-
|
50
|
+
def check_numbers(secret, numbers)
|
51
|
+
exact_matches, non_exact_matches = secret.zip(numbers).partition do |secret_number, input_number|
|
52
|
+
secret_number == input_number
|
53
|
+
end
|
54
|
+
|
55
|
+
result = Array.new(exact_matches.count, GOT_IT)
|
56
|
+
|
57
|
+
find_non_exact_matches(result, non_exact_matches) if non_exact_matches.any?
|
62
58
|
|
63
|
-
|
64
|
-
@game_numbers[:input][index] = nil
|
65
|
-
GUESS_PLACE
|
66
|
-
end.compact
|
59
|
+
result.join
|
67
60
|
end
|
68
61
|
|
69
|
-
def
|
70
|
-
|
71
|
-
|
62
|
+
def find_non_exact_matches(result, non_exact_matches)
|
63
|
+
secret, numbers = non_exact_matches.transpose
|
64
|
+
numbers.each do |number_element|
|
65
|
+
next unless secret.include? number_element
|
72
66
|
|
73
|
-
|
74
|
-
|
75
|
-
end.compact
|
67
|
+
result.push(NOT_YET) && secret.delete_at(secret.index(number_element))
|
68
|
+
end
|
76
69
|
end
|
77
70
|
|
78
|
-
def
|
79
|
-
|
71
|
+
def hint(secret)
|
72
|
+
secret.shuffle.pop
|
80
73
|
end
|
81
74
|
end
|
82
75
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CodebreakerVk
|
4
|
+
class GameConsole
|
5
|
+
include Validation
|
6
|
+
include Database
|
7
|
+
include Output
|
8
|
+
|
9
|
+
HINT = 'hint'
|
10
|
+
SAVE = 'save'
|
11
|
+
INPUT_DATA = /^[1-6]{4}$/.freeze
|
12
|
+
|
13
|
+
def initialize(name, difficulty)
|
14
|
+
@game = Game.new(name: name, difficulty: difficulty)
|
15
|
+
end
|
16
|
+
|
17
|
+
def start
|
18
|
+
loop do
|
19
|
+
break if @game.attempts.zero? || @game.win?
|
20
|
+
|
21
|
+
start_info(@game.attempts, @game.hints)
|
22
|
+
input = gets.chomp
|
23
|
+
case input
|
24
|
+
when GameMenu::EXIT then break close
|
25
|
+
when HINT then next puts @game.use_hint
|
26
|
+
when INPUT_DATA then puts @game.check(input)
|
27
|
+
else puts I18n.t(:wrong_process)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
puts I18n.t(:game_over)
|
31
|
+
statistics
|
32
|
+
end
|
33
|
+
|
34
|
+
def statistics
|
35
|
+
summary_info(@game.secret)
|
36
|
+
end_game
|
37
|
+
end
|
38
|
+
|
39
|
+
def end_game
|
40
|
+
if @game.win?
|
41
|
+
puts I18n.t(:win)
|
42
|
+
puts I18n.t(:save)
|
43
|
+
save_results if gets.chomp == SAVE
|
44
|
+
else
|
45
|
+
puts I18n.t(:lose)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def close
|
50
|
+
puts I18n.t(:goodbye)
|
51
|
+
exit
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'database'
|
4
|
+
|
5
|
+
module CodebreakerVk
|
6
|
+
class GameMenu
|
7
|
+
extend Validation
|
8
|
+
extend Database
|
9
|
+
extend Output
|
10
|
+
|
11
|
+
START = 'start'
|
12
|
+
RULES = 'rules'
|
13
|
+
STATISTICS = 'stats'
|
14
|
+
EXIT = 'exit'
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def welcome
|
18
|
+
puts I18n.t(:greeting)
|
19
|
+
run
|
20
|
+
end
|
21
|
+
|
22
|
+
def run
|
23
|
+
loop do
|
24
|
+
puts I18n.t(:menu)
|
25
|
+
case gets.chomp
|
26
|
+
when START then break registration
|
27
|
+
when RULES then rules
|
28
|
+
when STATISTICS then stats
|
29
|
+
when EXIT then break close
|
30
|
+
else puts I18n.t(:wrong_run)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def registration
|
36
|
+
GameConsole.new(choose_name, choose_difficulty).start
|
37
|
+
welcome
|
38
|
+
end
|
39
|
+
|
40
|
+
def choose_name
|
41
|
+
loop do
|
42
|
+
puts I18n.t(:choose_name)
|
43
|
+
name = gets.chomp
|
44
|
+
break close if name == EXIT
|
45
|
+
break name if valid_string_length?(name)
|
46
|
+
|
47
|
+
puts I18n.t(:wrong_name)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def choose_difficulty
|
52
|
+
loop do
|
53
|
+
puts I18n.t(:choose_difficulty)
|
54
|
+
input = gets.chomp
|
55
|
+
break close if input == EXIT
|
56
|
+
break input.to_sym if Game::DIFFICULTY_LEVEL.keys.include? input.to_sym
|
57
|
+
|
58
|
+
puts I18n.t(:wrong_difficulty)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def stats
|
63
|
+
return puts I18n.t(:no_stats) unless File.exist?('./seed.yaml')
|
64
|
+
|
65
|
+
table = load.sort_by { |row| [row.hints_total, row.attempts_used] }
|
66
|
+
table.each { |row| row.difficulty = Game::DIFFICULTY_LEVEL.key([row.attempts_total, row.hints_total]) }
|
67
|
+
puts table
|
68
|
+
end
|
69
|
+
|
70
|
+
def close
|
71
|
+
puts I18n.t(:goodbye)
|
72
|
+
exit
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CodebreakerVk
|
4
|
+
module Output
|
5
|
+
def start_info(attempts, hints)
|
6
|
+
puts I18n.t(:game_process, attempts: attempts, hints: hints)
|
7
|
+
end
|
8
|
+
|
9
|
+
def summary_info(secret)
|
10
|
+
puts I18n.t(:secret, secret: secret)
|
11
|
+
end
|
12
|
+
|
13
|
+
def rules
|
14
|
+
puts I18n.t(:rules)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CodebreakerVk
|
4
|
+
class TableData
|
5
|
+
attr_accessor :name, :difficulty, :attempts_total, :attempts_used, :hints_total, :hints_used
|
6
|
+
|
7
|
+
def initialize(name:, difficulty:, attempts_total:, attempts_used:, hints_total:, hints_used:)
|
8
|
+
@name = name
|
9
|
+
@difficulty = difficulty
|
10
|
+
@attempts_total = attempts_total
|
11
|
+
@attempts_used = attempts_used
|
12
|
+
@hints_total = hints_total
|
13
|
+
@hints_used = hints_used
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
I18n.t(:stats,
|
18
|
+
name: @name,
|
19
|
+
difficulty: @difficulty,
|
20
|
+
attempts_total: @attempts_total,
|
21
|
+
attempts_used: @attempts_used,
|
22
|
+
hints_total: @hints_total,
|
23
|
+
hints_used: @hints_used)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CodebreakerVk
|
4
|
+
module Validation
|
5
|
+
MIN_LETTERS = 3
|
6
|
+
MAX_LETTERS = 20
|
7
|
+
|
8
|
+
def valid_string_length?(name, min = MIN_LETTERS, max = MAX_LETTERS)
|
9
|
+
name.is_a?(String) && name.length.between?(min, max)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/main.rb
ADDED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: codebreaker_vk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- VKuzmich
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-08-
|
11
|
+
date: 2019-08-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -181,18 +181,17 @@ files:
|
|
181
181
|
- codebreaker_vk.gemspec
|
182
182
|
- config/i18n.rb
|
183
183
|
- config/locales/en.yml
|
184
|
+
- database.yaml
|
184
185
|
- lib/codebreaker_vk.rb
|
185
|
-
- lib/codebreaker_vk/
|
186
|
-
- lib/codebreaker_vk/console.rb
|
187
|
-
- lib/codebreaker_vk/difficulty.rb
|
186
|
+
- lib/codebreaker_vk/database.rb
|
188
187
|
- lib/codebreaker_vk/game.rb
|
189
|
-
- lib/codebreaker_vk/
|
190
|
-
- lib/codebreaker_vk/
|
191
|
-
- lib/codebreaker_vk/
|
192
|
-
- lib/codebreaker_vk/
|
193
|
-
- lib/codebreaker_vk/
|
194
|
-
- lib/codebreaker_vk/validating_data.rb
|
188
|
+
- lib/codebreaker_vk/game_console.rb
|
189
|
+
- lib/codebreaker_vk/game_menu.rb
|
190
|
+
- lib/codebreaker_vk/output.rb
|
191
|
+
- lib/codebreaker_vk/table_data.rb
|
192
|
+
- lib/codebreaker_vk/validation.rb
|
195
193
|
- lib/codebreaker_vk/version.rb
|
194
|
+
- main.rb
|
196
195
|
homepage: https://github.com/VKuzmich/codebreaker_vk
|
197
196
|
licenses:
|
198
197
|
- MIT
|
@@ -1,16 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module CodebreakerVk
|
4
|
-
class CheckErrors < ValidatingData
|
5
|
-
attr_reader :input, :errors
|
6
|
-
|
7
|
-
def initialize(input)
|
8
|
-
super()
|
9
|
-
@input = input
|
10
|
-
end
|
11
|
-
|
12
|
-
def validate
|
13
|
-
@errors << I18n.t('invalid.include_error') unless check_include?(@input, Console::COMMANDS.values)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
@@ -1,111 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module CodebreakerVk
|
4
|
-
class Console < ValidatingData
|
5
|
-
include Uploader
|
6
|
-
|
7
|
-
ACCEPT_SAVING_RESULT = 'yes'
|
8
|
-
COMMANDS = { rules: 'rules',
|
9
|
-
start: 'start',
|
10
|
-
stats: 'stats',
|
11
|
-
exit: 'exit' }.freeze
|
12
|
-
|
13
|
-
def greeting
|
14
|
-
Representer.greeting_msg
|
15
|
-
end
|
16
|
-
|
17
|
-
def main_menu
|
18
|
-
loop do
|
19
|
-
Representer.what_next_text
|
20
|
-
case make_valid_input_for_class(Navigator).input
|
21
|
-
when COMMANDS[:start] then return registration
|
22
|
-
when COMMANDS[:rules] then Representer.show_rules
|
23
|
-
when COMMANDS[:stats] then statistics
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
def statistics
|
31
|
-
loaded_db = load_db
|
32
|
-
loaded_db.empty? ? Representer.empty_db_msg : Representer.show_db(loaded_db)
|
33
|
-
end
|
34
|
-
|
35
|
-
def registration
|
36
|
-
Representer.what_name_msg
|
37
|
-
@user = make_valid_input_for_class(User)
|
38
|
-
@difficulty = choose_difficulty
|
39
|
-
@game = Game.new(@difficulty, @user)
|
40
|
-
make_guess
|
41
|
-
end
|
42
|
-
|
43
|
-
def choose_difficulty
|
44
|
-
Representer.select_difficulty_msg
|
45
|
-
loop do
|
46
|
-
finded_level = Difficulty.find(user_input)
|
47
|
-
return finded_level if finded_level
|
48
|
-
|
49
|
-
Representer.error_msg(I18n.t('invalid.include_error'))
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def make_guess
|
54
|
-
loop do
|
55
|
-
Representer.make_guess_msg
|
56
|
-
guess = make_valid_input_for_class(Guess)
|
57
|
-
guess.hint? ? show_hint : check_round_result(guess.as_array_of_numbers)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def check_round_result(guess)
|
62
|
-
return win if @game.win?(guess)
|
63
|
-
return lose if @game.lose?(guess)
|
64
|
-
|
65
|
-
round_result = @game.start_round(guess)
|
66
|
-
Representer.round_info_text(round_result, @game.attempts, @game.hints)
|
67
|
-
end
|
68
|
-
|
69
|
-
def show_hint
|
70
|
-
@game.hints.positive? ? Representer.showed_hint_msg(@game.hint) : Representer.zero_hints_msg
|
71
|
-
end
|
72
|
-
|
73
|
-
def lose
|
74
|
-
Representer.lose_msg
|
75
|
-
start_new_game
|
76
|
-
end
|
77
|
-
|
78
|
-
def win
|
79
|
-
Representer.win_msg
|
80
|
-
save_result if user_input == ACCEPT_SAVING_RESULT
|
81
|
-
start_new_game
|
82
|
-
end
|
83
|
-
|
84
|
-
def start_new_game
|
85
|
-
Console.new.main_menu
|
86
|
-
end
|
87
|
-
|
88
|
-
def save_result
|
89
|
-
save_to_db(@game.to_h)
|
90
|
-
end
|
91
|
-
|
92
|
-
def make_valid_input_for_class(klass)
|
93
|
-
loop do
|
94
|
-
input = klass.new(user_input)
|
95
|
-
return input if input.valid?
|
96
|
-
|
97
|
-
Representer.error_msg(input.errors.join(', '))
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def user_input
|
102
|
-
input = gets.chomp.downcase
|
103
|
-
input == COMMANDS[:exit] ? exit_console : input
|
104
|
-
end
|
105
|
-
|
106
|
-
def exit_console
|
107
|
-
Representer.goodbye
|
108
|
-
exit
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module CodebreakerVk
|
4
|
-
class Difficulty
|
5
|
-
attr_reader :level
|
6
|
-
|
7
|
-
DIFFICULTIES = {
|
8
|
-
simple: {
|
9
|
-
hints: 2,
|
10
|
-
attempts: 15,
|
11
|
-
level: 'simple'
|
12
|
-
},
|
13
|
-
|
14
|
-
middle: {
|
15
|
-
hints: 1,
|
16
|
-
attempts: 10,
|
17
|
-
level: 'middle'
|
18
|
-
},
|
19
|
-
|
20
|
-
hard: {
|
21
|
-
hints: 1,
|
22
|
-
attempts: 5,
|
23
|
-
level: 'hard'
|
24
|
-
}
|
25
|
-
}.freeze
|
26
|
-
|
27
|
-
def initialize(input)
|
28
|
-
@level = DIFFICULTIES[input]
|
29
|
-
end
|
30
|
-
|
31
|
-
def self.find(input)
|
32
|
-
input_as_key = input.to_sym
|
33
|
-
return unless DIFFICULTIES.key?(input_as_key)
|
34
|
-
|
35
|
-
new(input_as_key)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
@@ -1,79 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module CodebreakerVk
|
4
|
-
class GameMessages
|
5
|
-
class << self
|
6
|
-
def greeting_msg
|
7
|
-
puts I18n.t('console.greeting')
|
8
|
-
end
|
9
|
-
|
10
|
-
def goodbye
|
11
|
-
puts I18n.t(:goodbye)
|
12
|
-
end
|
13
|
-
|
14
|
-
def what_next_text
|
15
|
-
puts I18n.t('console.choose_the_command', stats: Console::COMMANDS[:stats], rules: Console::COMMANDS[:rules],
|
16
|
-
start: Console::COMMANDS[:start], exit: Console::COMMANDS[:exit])
|
17
|
-
end
|
18
|
-
|
19
|
-
def what_name_msg
|
20
|
-
puts I18n.t('console.what_name')
|
21
|
-
end
|
22
|
-
|
23
|
-
def select_difficulty_msg
|
24
|
-
puts I18n.t('console.select_difficulty', difficulties: Difficulty::DIFFICULTIES.keys.join(', '))
|
25
|
-
end
|
26
|
-
|
27
|
-
def make_guess_msg
|
28
|
-
puts I18n.t('console.make_guess')
|
29
|
-
end
|
30
|
-
|
31
|
-
def showed_hint_msg(showed)
|
32
|
-
puts I18n.t('console.showed_hint', showed: showed)
|
33
|
-
end
|
34
|
-
|
35
|
-
def zero_hints_msg
|
36
|
-
puts I18n.t('console.zero_hints')
|
37
|
-
end
|
38
|
-
|
39
|
-
def round_info_text(result, attempts, hints)
|
40
|
-
puts I18n.t('console.result', result: result)
|
41
|
-
puts I18n.t('console.left_attempts_and_hints', attempts: attempts, hints: hints)
|
42
|
-
puts I18n.t('console.make_guess')
|
43
|
-
puts I18n.t('console.enter_hint') if hints.positive?
|
44
|
-
end
|
45
|
-
|
46
|
-
def win_msg
|
47
|
-
puts I18n.t('console.win', yes: Console::ACCEPT_SAVING_RESULT)
|
48
|
-
end
|
49
|
-
|
50
|
-
def lose_msg
|
51
|
-
puts I18n.t('console.lose')
|
52
|
-
end
|
53
|
-
|
54
|
-
def empty_db_msg
|
55
|
-
puts I18n.t('console.empty_db')
|
56
|
-
end
|
57
|
-
|
58
|
-
def show_db(loaded_db)
|
59
|
-
sort_db(loaded_db).each_with_index do |user, index|
|
60
|
-
puts I18n.t('console.stats_user_info', position: index + 1, name: user[:name], level: user[:level])
|
61
|
-
puts I18n.t('console.stats_lefts', attempts: user[:left_attempts], all_attempts: user[:all_attempts],
|
62
|
-
hints: user[:left_hints], all_hints: user[:all_hints])
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def show_rules
|
67
|
-
puts I18n.t('console.rules')
|
68
|
-
end
|
69
|
-
|
70
|
-
def error_msg(error)
|
71
|
-
puts I18n.t('error', error: error)
|
72
|
-
end
|
73
|
-
|
74
|
-
def sort_db(loaded_db)
|
75
|
-
loaded_db.sort_by { |user| [user[:all_attempts], -user[:left_attempts], -user[:left_hints]] }
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'difficulty'
|
4
|
-
|
5
|
-
module CodebreakerVk
|
6
|
-
class GameProcess < ValidatingData
|
7
|
-
attr_reader :input, :errors
|
8
|
-
|
9
|
-
HINT = 'hint'
|
10
|
-
|
11
|
-
def initialize(input)
|
12
|
-
super()
|
13
|
-
@input = input
|
14
|
-
end
|
15
|
-
|
16
|
-
def validate
|
17
|
-
return if hint?
|
18
|
-
|
19
|
-
@errors << I18n.t('invalid.include_error') unless check_numbers?(@input, valid_numbers)
|
20
|
-
@errors << I18n.t('invalid.size_error') unless check_size?(@input, Game::CODE_SIZE)
|
21
|
-
end
|
22
|
-
|
23
|
-
def as_array_of_numbers
|
24
|
-
@as_array_of_numbers ||= @input.chars.map(&:to_i)
|
25
|
-
end
|
26
|
-
|
27
|
-
def hint?
|
28
|
-
@input == HINT
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def valid_numbers
|
34
|
-
Game::INCLUDE_IN_GAME_NUMBERS.map(&:to_s)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module CodebreakerVk
|
4
|
-
class GameUser < ValidatingData
|
5
|
-
attr_reader :name, :errors
|
6
|
-
|
7
|
-
VALID_NAME_SIZE = (3..20).freeze
|
8
|
-
|
9
|
-
def initialize(name)
|
10
|
-
super()
|
11
|
-
@name = name
|
12
|
-
end
|
13
|
-
|
14
|
-
def validate
|
15
|
-
@errors << I18n.t('invalid.cover_error') unless check_cover?(@name, VALID_NAME_SIZE)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module CodebreakerVk
|
4
|
-
module Uploader
|
5
|
-
PATH_FOLDER = './data_base/'
|
6
|
-
PATH_NAME = 'database'
|
7
|
-
PATH_FORMAT = '.yaml'
|
8
|
-
PATH = PATH_FOLDER + PATH_NAME + PATH_FORMAT
|
9
|
-
|
10
|
-
def load_db
|
11
|
-
File.exist?(PATH) ? YAML.load_stream(File.open(PATH)) : []
|
12
|
-
end
|
13
|
-
|
14
|
-
def save_to_db(results)
|
15
|
-
File.open(PATH, 'a') { |f| f.write results.to_yaml }
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module CodebreakerVk
|
4
|
-
module Validate
|
5
|
-
def check_cover?(cheackable, valid_numbers)
|
6
|
-
valid_numbers.cover?(cheackable.size)
|
7
|
-
end
|
8
|
-
|
9
|
-
def check_include?(cheackable, valid_collection)
|
10
|
-
valid_collection.include?(cheackable)
|
11
|
-
end
|
12
|
-
|
13
|
-
def check_size?(cheackable, valid_size)
|
14
|
-
cheackable.size == valid_size
|
15
|
-
end
|
16
|
-
|
17
|
-
def check_numbers?(cheackable, valid_numbers)
|
18
|
-
cheackable.each_char.all? { |guess_char| valid_numbers.include?(guess_char) }
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module CodebreakerVk
|
4
|
-
class ValidatingData
|
5
|
-
include Validate
|
6
|
-
|
7
|
-
def initialize
|
8
|
-
@errors = []
|
9
|
-
end
|
10
|
-
|
11
|
-
def validate
|
12
|
-
raise NotImplementedError
|
13
|
-
end
|
14
|
-
|
15
|
-
def valid?
|
16
|
-
validate
|
17
|
-
@errors.empty?
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|