GemCodebreaker 0.1.6 → 0.2.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 +4 -4
- data/.circleci/config.yml +1 -6
- data/.rspec +1 -0
- data/.rubocop.yml +10 -3
- data/Gemfile.lock +5 -2
- data/{GemCodebreaker.gemspec → gem_codebreaker.gemspec} +3 -2
- data/lib/{GemCodebreaker/classes/codemaker.rb → gem_codebreaker/classes/code_maker.rb} +1 -2
- data/lib/gem_codebreaker/classes/game.rb +73 -0
- data/lib/gem_codebreaker/classes/game_config.rb +65 -0
- data/lib/{GemCodebreaker → gem_codebreaker}/classes/game_statistic.rb +2 -3
- data/lib/gem_codebreaker/classes/serializer_deserializer.rb +27 -0
- data/lib/{GemCodebreaker → gem_codebreaker}/classes/user.rb +3 -4
- data/lib/{GemCodebreaker → gem_codebreaker}/config/i18n_locales.rb +0 -0
- data/lib/{GemCodebreaker/errors/unknow_level_error.rb → gem_codebreaker/errors/inclusion_error.rb} +1 -1
- data/lib/{GemCodebreaker/errors/name_length_error.rb → gem_codebreaker/errors/length_error.rb} +3 -3
- data/lib/{GemCodebreaker → gem_codebreaker}/errors/no_attempts_error.rb +0 -0
- data/lib/{GemCodebreaker → gem_codebreaker}/errors/no_hints_error.rb +0 -0
- data/lib/{GemCodebreaker → gem_codebreaker}/errors/value_range_error.rb +0 -0
- data/lib/{GemCodebreaker → gem_codebreaker}/errors/wrong_class_error.rb +0 -0
- data/lib/gem_codebreaker/gem_codebreaker.rb +17 -0
- data/lib/gem_codebreaker/modules/validator.rb +17 -0
- data/lib/{GemCodebreaker → gem_codebreaker}/version.rb +1 -1
- metadata +34 -22
- data/lib/GemCodebreaker/classes/game.rb +0 -85
- data/lib/GemCodebreaker/classes/game_config.rb +0 -19
- data/lib/GemCodebreaker/errors/incorrect_length_error.rb +0 -10
- data/lib/GemCodebreaker/gem_codebreaker.rb +0 -23
- data/lib/GemCodebreaker/modules/game_configurations.rb +0 -18
- data/lib/GemCodebreaker/modules/serializer_deserializer.rb +0 -26
- data/lib/GemCodebreaker/modules/validator.rb +0 -35
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8d128beb164e21cd400d768f1471fa4773bd12d298dc01e35f20aa2821e9f41b
|
|
4
|
+
data.tar.gz: 390ddb056e6a40e21e0303204a41e51569209a4868e534aa50db930ef72a0f07
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ff237177856acb512ccd3ac35c8ae5faeae3620b92975f9f150f2fc78625de14deda329bf98f816bd92909261992e3d97a6a933ac986ccc47f397092846b4468
|
|
7
|
+
data.tar.gz: e43687095f316b8a66019666ea56e1c195ef99789bf23ec012378fb3b804e533cdafa07af65c83b967362a39543959ccbbdf3e3ef8b781c90e2886ec0d174b71
|
data/.circleci/config.yml
CHANGED
|
@@ -38,12 +38,7 @@ commands:
|
|
|
38
38
|
steps:
|
|
39
39
|
- run:
|
|
40
40
|
name: run spec
|
|
41
|
-
command:
|
|
42
|
-
mkdir /tmp/test-results
|
|
43
|
-
TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)"
|
|
44
|
-
bundle exec rspec --format progress \
|
|
45
|
-
--out /tmp/test-results/rspec.xml \
|
|
46
|
-
$TEST_FILES
|
|
41
|
+
command: bundle exec rspec --format progress spec
|
|
47
42
|
|
|
48
43
|
jobs:
|
|
49
44
|
lintering:
|
data/.rspec
CHANGED
data/.rubocop.yml
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
require:
|
|
2
|
+
- rubocop-performance
|
|
2
3
|
- rubocop-rspec
|
|
3
4
|
|
|
4
5
|
AllCops:
|
|
5
6
|
TargetRubyVersion: 2.7.1
|
|
6
7
|
NewCops: enable
|
|
7
8
|
|
|
9
|
+
Style/Documentation:
|
|
10
|
+
Enabled: false
|
|
11
|
+
|
|
8
12
|
Layout/LineLength:
|
|
9
13
|
Max: 120
|
|
10
|
-
|
|
11
|
-
Max: 270
|
|
14
|
+
|
|
12
15
|
RSpec/MessageSpies:
|
|
13
16
|
EnforcedStyle: receive
|
|
17
|
+
|
|
18
|
+
RSpec/NestedGroups:
|
|
19
|
+
Max: 6
|
|
20
|
+
|
|
14
21
|
Metrics/BlockLength:
|
|
15
|
-
ExcludedMethods: ['describe', 'context']
|
|
22
|
+
ExcludedMethods: ['describe', 'context']
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
GemCodebreaker (0.1.
|
|
4
|
+
GemCodebreaker (0.1.9)
|
|
5
|
+
i18n (~> 1.8.3)
|
|
5
6
|
|
|
6
7
|
GEM
|
|
7
8
|
remote: https://rubygems.org/
|
|
@@ -45,6 +46,8 @@ GEM
|
|
|
45
46
|
unicode-display_width (>= 1.4.0, < 2.0)
|
|
46
47
|
rubocop-ast (0.0.3)
|
|
47
48
|
parser (>= 2.7.0.1)
|
|
49
|
+
rubocop-performance (1.6.1)
|
|
50
|
+
rubocop (>= 0.71.0)
|
|
48
51
|
rubocop-rspec (1.41.0)
|
|
49
52
|
rubocop (>= 0.68.1)
|
|
50
53
|
ruby-progressbar (1.10.1)
|
|
@@ -63,10 +66,10 @@ PLATFORMS
|
|
|
63
66
|
DEPENDENCIES
|
|
64
67
|
GemCodebreaker!
|
|
65
68
|
fasterer (~> 0.8.3)
|
|
66
|
-
i18n (~> 1.8.3)
|
|
67
69
|
rake (~> 13.0.1)
|
|
68
70
|
rspec (~> 3.7.0)
|
|
69
71
|
rubocop (~> 0.84.0)
|
|
72
|
+
rubocop-performance (~> 1.6.1)
|
|
70
73
|
rubocop-rspec (~> 1.41.0)
|
|
71
74
|
simplecov (~> 0.18.5)
|
|
72
75
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
lib = File.expand_path('lib', __dir__)
|
|
4
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
|
-
require_relative 'lib/
|
|
5
|
+
require_relative 'lib/gem_codebreaker/version'
|
|
6
6
|
|
|
7
7
|
Gem::Specification.new do |spec|
|
|
8
8
|
spec.name = 'GemCodebreaker'
|
|
@@ -28,10 +28,11 @@ Gem::Specification.new do |spec|
|
|
|
28
28
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
29
29
|
spec.require_paths = ['lib']
|
|
30
30
|
spec.add_development_dependency 'fasterer', '~> 0.8.3'
|
|
31
|
-
spec.
|
|
31
|
+
spec.add_dependency 'i18n', '~> 1.8.3'
|
|
32
32
|
spec.add_development_dependency 'rake', '~> 13.0.1'
|
|
33
33
|
spec.add_development_dependency 'rspec', '~> 3.7.0'
|
|
34
34
|
spec.add_development_dependency 'rubocop', '~> 0.84.0'
|
|
35
|
+
spec.add_development_dependency 'rubocop-performance', '~> 1.6.1'
|
|
35
36
|
spec.add_development_dependency 'rubocop-rspec', '~> 1.41.0'
|
|
36
37
|
spec.add_development_dependency 'simplecov', '~> 0.18.5'
|
|
37
38
|
end
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module GemCodebreaker
|
|
4
|
-
# Marker class for game
|
|
5
4
|
class CodeMaker
|
|
6
5
|
attr_reader :plus_match, :minus_match, :empty_match
|
|
7
6
|
|
|
@@ -32,7 +31,7 @@ module GemCodebreaker
|
|
|
32
31
|
@game_code.each do |secret_code_item|
|
|
33
32
|
if @user_code.include?(secret_code_item)
|
|
34
33
|
@minus_match += 1
|
|
35
|
-
@user_code.
|
|
34
|
+
@user_code.delete_at(@user_code.index(secret_code_item))
|
|
36
35
|
else
|
|
37
36
|
@empty_match += 1
|
|
38
37
|
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GemCodebreaker
|
|
4
|
+
class Game
|
|
5
|
+
LENGTH_OF_SECRET_CODE = 4
|
|
6
|
+
DIGIT_RANGE = (1..6).freeze
|
|
7
|
+
|
|
8
|
+
include Validator
|
|
9
|
+
attr_reader :statistic, :matcher_hash, :config
|
|
10
|
+
|
|
11
|
+
def initialize(user, level)
|
|
12
|
+
@config = GameConfig.new(user, level)
|
|
13
|
+
@statistic = []
|
|
14
|
+
secrete_code
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def guess_start(input_code)
|
|
18
|
+
guess_valid?(input_code)
|
|
19
|
+
raise GemCodebreaker::NoAttemptsError if @config.attempts_used?
|
|
20
|
+
|
|
21
|
+
@config.increase_user_attempts
|
|
22
|
+
user_code = input_code.chars.map(&:to_i)
|
|
23
|
+
code_marker = CodeMaker.new(@secrete_code, user_code)
|
|
24
|
+
code_marker.calculate_result_code
|
|
25
|
+
@matcher_hash = { plus: code_marker.plus_match, minus: code_marker.minus_match, empty: code_marker.empty_match }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def win_game?
|
|
29
|
+
@matcher_hash[:plus] == LENGTH_OF_SECRET_CODE
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def game_over?
|
|
33
|
+
@config.attempts_used? && win_game? == false
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def show_secrete_code
|
|
37
|
+
@secrete_code if win_game? || game_over?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def hint
|
|
41
|
+
raise GemCodebreaker::NoHintsError if @config.hints_used?
|
|
42
|
+
|
|
43
|
+
@config.increase_user_hints
|
|
44
|
+
@secrete_code.sample
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def restart_game
|
|
48
|
+
@config.reset_user_attempts_hints
|
|
49
|
+
@secrete_code = false
|
|
50
|
+
secrete_code
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def save_user_statistic
|
|
54
|
+
@statistic << GameStatistic.new(self)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def guess_valid?(user_code)
|
|
60
|
+
validate_class(user_code, String)
|
|
61
|
+
raise GemCodebreaker::LengthError, I18n.t('errors.incorrect_length') if user_code.length != LENGTH_OF_SECRET_CODE
|
|
62
|
+
unless user_code[/\A[#{DIGIT_RANGE.first}-#{DIGIT_RANGE.last}]{#{LENGTH_OF_SECRET_CODE}}\z/]
|
|
63
|
+
raise GemCodebreaker::ValueRangeError
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
true
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def secrete_code
|
|
70
|
+
@secrete_code ||= (1..LENGTH_OF_SECRET_CODE).map { rand DIGIT_RANGE }
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GemCodebreaker
|
|
4
|
+
class GameConfig
|
|
5
|
+
GAME_LEVEL = {
|
|
6
|
+
easy: {
|
|
7
|
+
attempts: 15,
|
|
8
|
+
hints: 2
|
|
9
|
+
},
|
|
10
|
+
medium: {
|
|
11
|
+
attempts: 10,
|
|
12
|
+
hints: 1
|
|
13
|
+
},
|
|
14
|
+
hell: {
|
|
15
|
+
attempts: 5,
|
|
16
|
+
hints: 1
|
|
17
|
+
},
|
|
18
|
+
strong_hell: {
|
|
19
|
+
attempts: 1,
|
|
20
|
+
hints: 0
|
|
21
|
+
}
|
|
22
|
+
}.freeze
|
|
23
|
+
|
|
24
|
+
include Validator
|
|
25
|
+
|
|
26
|
+
attr_reader :user_nick_name, :difficulty, :max_attempts, :max_hints, :user_attempts, :user_hints
|
|
27
|
+
|
|
28
|
+
def initialize(user, level)
|
|
29
|
+
check_difficulty(level)
|
|
30
|
+
@user_nick_name = user.nick_name
|
|
31
|
+
@difficulty = level
|
|
32
|
+
@max_attempts = GAME_LEVEL[level][:attempts]
|
|
33
|
+
@max_hints = GAME_LEVEL[level][:hints]
|
|
34
|
+
@user_attempts = 0
|
|
35
|
+
@user_hints = 0
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def increase_user_attempts
|
|
39
|
+
@user_attempts += 1
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def increase_user_hints
|
|
43
|
+
@user_hints += 1
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def attempts_used?
|
|
47
|
+
@user_attempts >= @max_attempts
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def hints_used?
|
|
51
|
+
@user_hints >= @max_hints
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def reset_user_attempts_hints
|
|
55
|
+
@user_attempts = 0
|
|
56
|
+
@user_hints = 0
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def check_difficulty(difficulty)
|
|
62
|
+
validate_inclusion(GAME_LEVEL.keys, difficulty)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module GemCodebreaker
|
|
4
|
-
# Game statistic class
|
|
5
4
|
class GameStatistic
|
|
6
5
|
attr_reader :user_nickname, :difficulty, :attempts_total, :attempts_used, :hints_total, :hints_used
|
|
7
6
|
|
|
@@ -9,9 +8,9 @@ module GemCodebreaker
|
|
|
9
8
|
@user_nickname = game.config.user_nick_name
|
|
10
9
|
@difficulty = game.config.difficulty
|
|
11
10
|
@attempts_total = game.config.max_attempts
|
|
12
|
-
@attempts_used = game.
|
|
11
|
+
@attempts_used = game.config.user_attempts
|
|
13
12
|
@hints_total = game.config.max_hints
|
|
14
|
-
@hints_used = game.
|
|
13
|
+
@hints_used = game.config.user_hints
|
|
15
14
|
end
|
|
16
15
|
end
|
|
17
16
|
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GemCodebreaker
|
|
4
|
+
class SerializerDeserializer
|
|
5
|
+
def initialize
|
|
6
|
+
@file_name = 'game_statistic.yml'
|
|
7
|
+
@statistic_dir = 'data'
|
|
8
|
+
@storage_path = File.expand_path("./#{@statistic_dir}/#{@file_name}")
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def data_dir_get
|
|
12
|
+
data_directory = File.dirname(@storage_path)
|
|
13
|
+
Dir.mkdir(data_directory) unless File.directory?(data_directory)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def load_game_statistic
|
|
17
|
+
File.exist?(@storage_path) ? YAML.load_file(@storage_path) : []
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def save_game_statistic(game_statistic)
|
|
21
|
+
save_game_stat = game_statistic | load_game_statistic
|
|
22
|
+
File.open(@storage_path, 'w') do |file|
|
|
23
|
+
YAML.dump(save_game_stat, file)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module GemCodebreaker
|
|
4
|
-
# User class for game
|
|
5
4
|
class User
|
|
6
5
|
include Validator
|
|
7
|
-
|
|
6
|
+
attr_reader :nick_name
|
|
8
7
|
|
|
9
8
|
def initialize(nick_name)
|
|
10
9
|
@nick_name = nick_name
|
|
11
10
|
end
|
|
12
11
|
|
|
13
12
|
def nickname_valid?
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
validate_class(@nick_name, String)
|
|
14
|
+
validate_length(@nick_name, (3..20), I18n.t('errors.name_length'))
|
|
16
15
|
true
|
|
17
16
|
end
|
|
18
17
|
end
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'gem_codebreaker/config/i18n_locales'
|
|
4
|
+
require 'gem_codebreaker/version'
|
|
5
|
+
require 'gem_codebreaker/errors/length_error'
|
|
6
|
+
require 'gem_codebreaker/errors/no_attempts_error'
|
|
7
|
+
require 'gem_codebreaker/errors/no_hints_error'
|
|
8
|
+
require 'gem_codebreaker/errors/inclusion_error'
|
|
9
|
+
require 'gem_codebreaker/errors/value_range_error'
|
|
10
|
+
require 'gem_codebreaker/errors/wrong_class_error'
|
|
11
|
+
require 'gem_codebreaker/modules/validator'
|
|
12
|
+
require 'gem_codebreaker/classes/serializer_deserializer'
|
|
13
|
+
require 'gem_codebreaker/classes/user'
|
|
14
|
+
require 'gem_codebreaker/classes/code_maker'
|
|
15
|
+
require 'gem_codebreaker/classes/game_config'
|
|
16
|
+
require 'gem_codebreaker/classes/game_statistic'
|
|
17
|
+
require 'gem_codebreaker/classes/game'
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GemCodebreaker
|
|
4
|
+
module Validator
|
|
5
|
+
def validate_inclusion(valid_values, check_value)
|
|
6
|
+
raise GemCodebreaker::InclusionError unless valid_values.include?(check_value)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def validate_class(user_code, klass)
|
|
10
|
+
raise GemCodebreaker::WrongClassError unless user_code.is_a?(klass)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def validate_length(check_value, valid_values, errors_message)
|
|
14
|
+
raise GemCodebreaker::LengthError, errors_message unless valid_values.member?(check_value.length)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: GemCodebreaker
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vasyl
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2020-
|
|
11
|
+
date: 2020-08-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: fasterer
|
|
@@ -31,7 +31,7 @@ dependencies:
|
|
|
31
31
|
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
33
|
version: 1.8.3
|
|
34
|
-
type: :
|
|
34
|
+
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
@@ -80,6 +80,20 @@ dependencies:
|
|
|
80
80
|
- - "~>"
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
82
|
version: 0.84.0
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: rubocop-performance
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: 1.6.1
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: 1.6.1
|
|
83
97
|
- !ruby/object:Gem::Dependency
|
|
84
98
|
name: rubocop-rspec
|
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -120,31 +134,29 @@ files:
|
|
|
120
134
|
- ".gitignore"
|
|
121
135
|
- ".rspec"
|
|
122
136
|
- ".rubocop.yml"
|
|
123
|
-
- GemCodebreaker.gemspec
|
|
124
137
|
- Gemfile
|
|
125
138
|
- Gemfile.lock
|
|
126
139
|
- LICENSE.txt
|
|
127
140
|
- README.md
|
|
128
141
|
- bin/console
|
|
129
142
|
- bin/setup
|
|
130
|
-
-
|
|
131
|
-
- lib/
|
|
132
|
-
- lib/
|
|
133
|
-
- lib/
|
|
134
|
-
- lib/
|
|
135
|
-
- lib/
|
|
136
|
-
- lib/
|
|
137
|
-
- lib/
|
|
138
|
-
- lib/
|
|
139
|
-
- lib/
|
|
140
|
-
- lib/
|
|
141
|
-
- lib/
|
|
142
|
-
- lib/
|
|
143
|
-
- lib/
|
|
144
|
-
- lib/
|
|
145
|
-
- lib/
|
|
146
|
-
- lib/
|
|
147
|
-
- lib/GemCodebreaker/version.rb
|
|
143
|
+
- gem_codebreaker.gemspec
|
|
144
|
+
- lib/gem_codebreaker/classes/code_maker.rb
|
|
145
|
+
- lib/gem_codebreaker/classes/game.rb
|
|
146
|
+
- lib/gem_codebreaker/classes/game_config.rb
|
|
147
|
+
- lib/gem_codebreaker/classes/game_statistic.rb
|
|
148
|
+
- lib/gem_codebreaker/classes/serializer_deserializer.rb
|
|
149
|
+
- lib/gem_codebreaker/classes/user.rb
|
|
150
|
+
- lib/gem_codebreaker/config/i18n_locales.rb
|
|
151
|
+
- lib/gem_codebreaker/errors/inclusion_error.rb
|
|
152
|
+
- lib/gem_codebreaker/errors/length_error.rb
|
|
153
|
+
- lib/gem_codebreaker/errors/no_attempts_error.rb
|
|
154
|
+
- lib/gem_codebreaker/errors/no_hints_error.rb
|
|
155
|
+
- lib/gem_codebreaker/errors/value_range_error.rb
|
|
156
|
+
- lib/gem_codebreaker/errors/wrong_class_error.rb
|
|
157
|
+
- lib/gem_codebreaker/gem_codebreaker.rb
|
|
158
|
+
- lib/gem_codebreaker/modules/validator.rb
|
|
159
|
+
- lib/gem_codebreaker/version.rb
|
|
148
160
|
homepage: https://github.com/vasivaas/GemCodebreaker
|
|
149
161
|
licenses:
|
|
150
162
|
- MIT
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module GemCodebreaker
|
|
4
|
-
# Game class
|
|
5
|
-
class Game
|
|
6
|
-
LENGTH_OF_SECRET_CODE = 4
|
|
7
|
-
DIGIT_RANGE = (1..6).freeze
|
|
8
|
-
|
|
9
|
-
include GameConfigurations
|
|
10
|
-
include Validator
|
|
11
|
-
attr_reader :attempts, :hints, :secrete_code, :statistic, :matcher_hash
|
|
12
|
-
attr_accessor :config
|
|
13
|
-
|
|
14
|
-
def initialize(user, level)
|
|
15
|
-
@config = GameConfig.new(user, level.to_sym)
|
|
16
|
-
@attempts = @config.user_attempts
|
|
17
|
-
@hints = @config.user_hints
|
|
18
|
-
@statistic = []
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def check_configuration
|
|
22
|
-
validate_game_level(@config.difficulty)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def generate_secret_code
|
|
26
|
-
@secrete_code = (1..LENGTH_OF_SECRET_CODE).map { rand DIGIT_RANGE }
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def guess_valid?(user_code)
|
|
30
|
-
validate_string(user_code)
|
|
31
|
-
validate_guess_length if user_code.length != LENGTH_OF_SECRET_CODE
|
|
32
|
-
validate_chars_range unless user_code[/\A[1-6]{4}\z/]
|
|
33
|
-
true
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def guess_start(input_code)
|
|
37
|
-
validate_number_of_attempts if @attempts >= @config.max_attempts
|
|
38
|
-
|
|
39
|
-
@attempts += 1
|
|
40
|
-
user_code = convert_string_to_array(input_code)
|
|
41
|
-
code_marker = CodeMaker.new(@secrete_code, user_code)
|
|
42
|
-
code_marker.calculate_result_code
|
|
43
|
-
@matcher_hash = { plus: code_marker.plus_match, minus: code_marker.minus_match, empty: code_marker.empty_match }
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def win_game?
|
|
47
|
-
@matcher_hash[:plus] == LENGTH_OF_SECRET_CODE
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def game_over?
|
|
51
|
-
(@attempts >= @config.max_attempts) && win_game? == false
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def hints_used?
|
|
55
|
-
@hints >= @config.max_hints
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def hint
|
|
59
|
-
validate_number_of_hints if hints_used?
|
|
60
|
-
|
|
61
|
-
@hints += 1
|
|
62
|
-
@secrete_code.sample
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def reset
|
|
66
|
-
@attempts = 0
|
|
67
|
-
@hints = 0
|
|
68
|
-
generate_secret_code
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def save_user_statistic
|
|
72
|
-
@statistic << current_user_statistic
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
private
|
|
76
|
-
|
|
77
|
-
def current_user_statistic
|
|
78
|
-
GameStatistic.new(self)
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def convert_string_to_array(user_code)
|
|
82
|
-
user_code.chars.map(&:to_i)
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
end
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module GemCodebreaker
|
|
4
|
-
# Configuration class for game
|
|
5
|
-
class GameConfig
|
|
6
|
-
include GameConfigurations
|
|
7
|
-
attr_reader :user_attempts, :user_hints
|
|
8
|
-
attr_accessor :user_nick_name, :difficulty, :max_attempts, :max_hints
|
|
9
|
-
|
|
10
|
-
def initialize(user, level)
|
|
11
|
-
@user_nick_name = user.nick_name
|
|
12
|
-
@difficulty = level
|
|
13
|
-
@max_attempts = GAME_LEVEL[level][:attempts]
|
|
14
|
-
@max_hints = GAME_LEVEL[level][:hints]
|
|
15
|
-
@user_attempts = 0
|
|
16
|
-
@user_hints = 0
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'GemCodebreaker/config/i18n_locales'
|
|
4
|
-
require 'GemCodebreaker/version'
|
|
5
|
-
require 'GemCodebreaker/errors/incorrect_length_error'
|
|
6
|
-
require 'GemCodebreaker/errors/name_length_error'
|
|
7
|
-
require 'GemCodebreaker/errors/no_attempts_error'
|
|
8
|
-
require 'GemCodebreaker/errors/no_hints_error'
|
|
9
|
-
require 'GemCodebreaker/errors/unknow_level_error'
|
|
10
|
-
require 'GemCodebreaker/errors/value_range_error'
|
|
11
|
-
require 'GemCodebreaker/errors/wrong_class_error'
|
|
12
|
-
require 'GemCodebreaker/modules/validator'
|
|
13
|
-
require 'GemCodebreaker/modules/serializer_deserializer'
|
|
14
|
-
require 'GemCodebreaker/modules/game_configurations'
|
|
15
|
-
require 'GemCodebreaker/classes/user'
|
|
16
|
-
require 'GemCodebreaker/classes/codemaker'
|
|
17
|
-
require 'GemCodebreaker/classes/game_config'
|
|
18
|
-
require 'GemCodebreaker/classes/game_statistic'
|
|
19
|
-
require 'GemCodebreaker/classes/game'
|
|
20
|
-
|
|
21
|
-
module GemCodebreaker
|
|
22
|
-
class Error < StandardError; end
|
|
23
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module GemCodebreaker
|
|
4
|
-
module GameConfigurations
|
|
5
|
-
GAME_LEVEL = { easy:
|
|
6
|
-
{ attempts: 15,
|
|
7
|
-
hints: 2 },
|
|
8
|
-
medium:
|
|
9
|
-
{ attempts: 10,
|
|
10
|
-
hints: 1 },
|
|
11
|
-
hell:
|
|
12
|
-
{ attempts: 5,
|
|
13
|
-
hints: 1 },
|
|
14
|
-
strong_hell:
|
|
15
|
-
{ attempts: 1,
|
|
16
|
-
hints: 0 } }.freeze
|
|
17
|
-
end
|
|
18
|
-
end
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module GemCodebreaker
|
|
4
|
-
# Serialize and Deserialize data for game statistic
|
|
5
|
-
module SerializerDeserializer
|
|
6
|
-
FILE_NAME = 'game_statistic.yml'
|
|
7
|
-
STATISTIC_DIR = 'data'
|
|
8
|
-
STORAGE_PATH = File.expand_path("./#{STATISTIC_DIR}/#{FILE_NAME}")
|
|
9
|
-
|
|
10
|
-
def data_dir_get
|
|
11
|
-
data_directory = File.dirname(STORAGE_PATH)
|
|
12
|
-
Dir.mkdir(data_directory) unless File.directory?(data_directory)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def load_game_statistic
|
|
16
|
-
File.exist?(STORAGE_PATH) ? YAML.load_file(STORAGE_PATH) : []
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def save_game_statistic(game_statistic)
|
|
20
|
-
save_game_stat = game_statistic | load_game_statistic
|
|
21
|
-
File.open(STORAGE_PATH, 'w') do |file|
|
|
22
|
-
YAML.dump(save_game_stat, file)
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module GemCodebreaker
|
|
4
|
-
# Validator
|
|
5
|
-
module Validator
|
|
6
|
-
def validate_game_level(game_level)
|
|
7
|
-
level = %i[easy medium hell strong_hell]
|
|
8
|
-
raise GemCodebreaker::UnknowLevelError unless level.include?(game_level)
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def validate_number_of_hints
|
|
12
|
-
raise GemCodebreaker::NoHintsError
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def validate_number_of_attempts
|
|
16
|
-
raise GemCodebreaker::NoAttemptsError
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def validate_string(object)
|
|
20
|
-
raise GemCodebreaker::WrongClassError unless object.is_a?(String)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def validate_guess_length
|
|
24
|
-
raise GemCodebreaker::IncorrectLengthError
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def validate_chars_range
|
|
28
|
-
raise GemCodebreaker::ValueRangeError
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def validate_nickname_length(nickname)
|
|
32
|
-
raise GemCodebreaker::NameLengthError if nickname.length < 3 || nickname.length > 20
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|