cb-core 0.1.2 → 0.1.8
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/.fasterer.yml +1 -1
- data/.gitignore +2 -0
- data/.rubocop.yml +4 -0
- data/Gemfile.lock +5 -1
- data/codebreaker.gemspec +12 -10
- data/config/i18n.rb +3 -0
- data/config/locales/en.yml +4 -0
- data/lib/codebreaker.rb +11 -6
- data/lib/codebreaker/create_stat_service.rb +24 -0
- data/lib/codebreaker/difficulty.rb +11 -0
- data/lib/codebreaker/difficulty_factory.rb +4 -4
- data/lib/codebreaker/errors/game_save_error.rb +9 -0
- data/lib/codebreaker/game.rb +37 -15
- data/lib/codebreaker/mixins/validator.rb +12 -0
- data/lib/codebreaker/player.rb +6 -7
- data/lib/codebreaker/secret_code_matcher.rb +2 -11
- data/lib/codebreaker/stats_storage.rb +22 -0
- data/lib/codebreaker/version.rb +1 -1
- metadata +23 -8
- data/lib/codebreaker/difficulties/base.rb +0 -13
- data/lib/codebreaker/difficulties/easy.rb +0 -13
- data/lib/codebreaker/difficulties/hell.rb +0 -13
- data/lib/codebreaker/difficulties/medium.rb +0 -13
- data/lib/codebreaker/hint.rb +0 -14
- data/lib/codebreaker/mixins/validatable.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 10d77f2204399e688b8fdc7682608c67a20635e2081089bb19ccb67e937ab8e5
|
4
|
+
data.tar.gz: 8b1beda09989c6570219bd385d4f280772c62990471367b3d561a36357cae59d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc43320fc01a5bd588122b13ef482017b8a067efeccd37d53fa1e530477cea056e99e00b349ecfdfa9a0ba12b22b11ad4b99f0e957fbf0f05b204fb7974506b3
|
7
|
+
data.tar.gz: 62b52ad66559cfbb93e49d3269ef87e20db0673cf3aff4885d88ab81aac26829ccb3876d1728b43b668f1326f3253050ba202da2cefbc0979bc359de01531aae
|
data/.fasterer.yml
CHANGED
data/.rubocop.yml
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,18 +1,22 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
cb-core (0.1.
|
4
|
+
cb-core (0.1.8)
|
5
|
+
i18n (~> 1.8.9)
|
5
6
|
|
6
7
|
GEM
|
7
8
|
remote: https://rubygems.org/
|
8
9
|
specs:
|
9
10
|
ast (2.4.2)
|
10
11
|
colorize (0.8.1)
|
12
|
+
concurrent-ruby (1.1.8)
|
11
13
|
diff-lcs (1.4.4)
|
12
14
|
docile (1.3.5)
|
13
15
|
fasterer (0.8.3)
|
14
16
|
colorize (~> 0.7)
|
15
17
|
ruby_parser (>= 3.14.1)
|
18
|
+
i18n (1.8.10)
|
19
|
+
concurrent-ruby (~> 1.0)
|
16
20
|
parallel (1.20.1)
|
17
21
|
parser (3.0.0.0)
|
18
22
|
ast (~> 2.4.1)
|
data/codebreaker.gemspec
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
require_relative 'lib/codebreaker/version'
|
2
2
|
|
3
3
|
Gem::Specification.new do |spec|
|
4
|
-
spec.name
|
5
|
-
spec.version
|
6
|
-
spec.authors
|
7
|
-
spec.email
|
4
|
+
spec.name = 'cb-core'
|
5
|
+
spec.version = Codebreaker::VERSION
|
6
|
+
spec.authors = ['Dmytro Yakupov']
|
7
|
+
spec.email = ['yakupov.dmitrij@gmail.com']
|
8
8
|
|
9
|
-
spec.summary
|
10
|
-
spec.description
|
11
|
-
spec.homepage
|
12
|
-
spec.license
|
9
|
+
spec.summary = 'Core'
|
10
|
+
spec.description = 'Contains a core logic.'
|
11
|
+
spec.homepage = 'https://github.com/YakupovDima/rubygarage-codebreaker-core'
|
12
|
+
spec.license = 'MIT'
|
13
13
|
spec.required_ruby_version = Gem::Requirement.new('>= 2.7.0')
|
14
14
|
|
15
15
|
spec.metadata['homepage_uri'] = spec.homepage
|
@@ -20,10 +20,12 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
21
21
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
22
22
|
end
|
23
|
-
spec.bindir
|
24
|
-
spec.executables
|
23
|
+
spec.bindir = 'exe'
|
24
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
25
25
|
spec.require_paths = ['lib']
|
26
26
|
|
27
|
+
spec.add_dependency 'i18n', '~> 1.8.9'
|
28
|
+
|
27
29
|
spec.add_development_dependency 'fasterer', '~> 0.8.3'
|
28
30
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
29
31
|
spec.add_development_dependency 'rspec_junit_formatter', '~> 0.4'
|
data/config/i18n.rb
ADDED
data/lib/codebreaker.rb
CHANGED
@@ -1,19 +1,24 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'yaml'
|
3
|
+
require 'i18n'
|
4
|
+
|
5
|
+
require_relative '../config/i18n'
|
6
|
+
|
7
|
+
require_relative 'codebreaker/difficulty'
|
1
8
|
require_relative 'codebreaker/difficulty_factory'
|
2
9
|
require_relative 'codebreaker/difficulty_type'
|
3
|
-
require_relative 'codebreaker/difficulties/base'
|
4
|
-
require_relative 'codebreaker/difficulties/easy'
|
5
|
-
require_relative 'codebreaker/difficulties/medium'
|
6
|
-
require_relative 'codebreaker/difficulties/hell'
|
7
10
|
require_relative 'codebreaker/errors/game_over_error'
|
11
|
+
require_relative 'codebreaker/errors/game_save_error'
|
8
12
|
require_relative 'codebreaker/errors/invalid_difficulty_error'
|
9
13
|
require_relative 'codebreaker/errors/no_hints_error'
|
10
14
|
require_relative 'codebreaker/errors/validation_length_error'
|
15
|
+
require_relative 'codebreaker/create_stat_service'
|
11
16
|
require_relative 'codebreaker/game'
|
12
|
-
require_relative 'codebreaker/
|
13
|
-
require_relative 'codebreaker/mixins/validatable'
|
17
|
+
require_relative 'codebreaker/mixins/validator'
|
14
18
|
require_relative 'codebreaker/player'
|
15
19
|
require_relative 'codebreaker/secret_code_generator'
|
16
20
|
require_relative 'codebreaker/secret_code_matcher'
|
21
|
+
require_relative 'codebreaker/stats_storage'
|
17
22
|
require_relative 'codebreaker/version'
|
18
23
|
|
19
24
|
module Codebreaker
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Codebreaker
|
2
|
+
class CreateStatService
|
3
|
+
def initialize(game)
|
4
|
+
@game = game
|
5
|
+
end
|
6
|
+
|
7
|
+
def call
|
8
|
+
StatsStorage.new.add(stat_data)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def stat_data
|
14
|
+
{
|
15
|
+
name: @game.player.name,
|
16
|
+
difficulty: @game.difficulty.type,
|
17
|
+
attempts_total: @game.difficulty.attempts,
|
18
|
+
attempts_used: @game.difficulty.attempts - @game.attempts_left,
|
19
|
+
hints_total: @game.difficulty.hints,
|
20
|
+
hints_used: @game.difficulty.hints - @game.hints_left
|
21
|
+
}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -3,13 +3,13 @@ module Codebreaker
|
|
3
3
|
def self.build(type)
|
4
4
|
case type
|
5
5
|
when DifficultyType::EASY
|
6
|
-
|
6
|
+
Difficulty.new(DifficultyType::EASY, 15, 2)
|
7
7
|
when DifficultyType::MEDIUM
|
8
|
-
|
8
|
+
Difficulty.new(DifficultyType::MEDIUM, 10, 1)
|
9
9
|
when DifficultyType::HELL
|
10
|
-
|
10
|
+
Difficulty.new(DifficultyType::HELL, 5, 1)
|
11
11
|
else
|
12
|
-
raise Errors::InvalidDifficultyError,
|
12
|
+
raise Errors::InvalidDifficultyError, I18n.t('errors.invalid_difficulty', type: type)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
data/lib/codebreaker/game.rb
CHANGED
@@ -1,26 +1,34 @@
|
|
1
1
|
module Codebreaker
|
2
2
|
class Game
|
3
|
-
attr_reader :
|
3
|
+
attr_reader :player, :attempts_left
|
4
4
|
|
5
5
|
def initialize(player, difficulty_type)
|
6
6
|
@player = player
|
7
|
-
@
|
8
|
-
@
|
9
|
-
@hint = Hint.new(@difficulty, @secret_code)
|
10
|
-
@attempts_used = 0
|
7
|
+
@difficulty_type = difficulty_type
|
8
|
+
@attempts_left = difficulty.attempts
|
11
9
|
@is_win = false
|
12
10
|
end
|
13
11
|
|
12
|
+
def secret_code
|
13
|
+
@secret_code ||= SecretCodeGenerator.new.call
|
14
|
+
end
|
15
|
+
|
16
|
+
def difficulty
|
17
|
+
@difficulty ||= DifficultyFactory.build(@difficulty_type)
|
18
|
+
end
|
19
|
+
|
14
20
|
def take_hint
|
15
21
|
ensure_game_is_not_over
|
16
|
-
|
22
|
+
raise Errors::NoHintsError if hints.empty?
|
23
|
+
|
24
|
+
hints.pop
|
17
25
|
end
|
18
26
|
|
19
27
|
def guess_secret_code(guess)
|
20
28
|
ensure_game_is_not_over
|
21
|
-
@
|
22
|
-
matching = SecretCodeMatcher.new(
|
23
|
-
check_win(
|
29
|
+
@attempts_left -= 1
|
30
|
+
matching = SecretCodeMatcher.new(secret_code, guess).call
|
31
|
+
check_win(guess)
|
24
32
|
matching
|
25
33
|
end
|
26
34
|
|
@@ -32,22 +40,36 @@ module Codebreaker
|
|
32
40
|
@is_win
|
33
41
|
end
|
34
42
|
|
43
|
+
def over?
|
44
|
+
win? || lose?
|
45
|
+
end
|
46
|
+
|
47
|
+
def hints_left
|
48
|
+
hints.count
|
49
|
+
end
|
50
|
+
|
51
|
+
def save_result
|
52
|
+
raise Errors::GameSaveError unless win?
|
53
|
+
|
54
|
+
CreateStatService.new(self).call
|
55
|
+
end
|
56
|
+
|
35
57
|
private
|
36
58
|
|
37
|
-
def
|
38
|
-
|
59
|
+
def hints
|
60
|
+
@hints ||= secret_code.sample(difficulty.hints)
|
39
61
|
end
|
40
62
|
|
41
63
|
def no_attempts?
|
42
|
-
|
64
|
+
attempts_left.zero?
|
43
65
|
end
|
44
66
|
|
45
67
|
def ensure_game_is_not_over
|
46
|
-
raise Errors::GameOverError if
|
68
|
+
raise Errors::GameOverError if over?
|
47
69
|
end
|
48
70
|
|
49
|
-
def check_win(
|
50
|
-
@is_win = true if
|
71
|
+
def check_win(guess)
|
72
|
+
@is_win = true if guess == secret_code
|
51
73
|
end
|
52
74
|
end
|
53
75
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Codebreaker
|
2
|
+
module Mixins
|
3
|
+
module Validator
|
4
|
+
def validate_length(value, min, max)
|
5
|
+
return if value.instance_of?(String) && value.size.between?(min, max)
|
6
|
+
|
7
|
+
raise Codebreaker::Errors::ValidationLengthError,
|
8
|
+
I18n.t('errors.validation_length', value: value, min: min, max: max)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/lib/codebreaker/player.rb
CHANGED
@@ -1,21 +1,20 @@
|
|
1
1
|
module Codebreaker
|
2
2
|
class Player
|
3
|
-
include Mixins::
|
3
|
+
include Mixins::Validator
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
NAME_SIZE = {
|
6
|
+
min: 3,
|
7
|
+
max: 20
|
8
|
+
}.freeze
|
7
9
|
|
8
10
|
attr_reader :name
|
9
11
|
|
10
12
|
def initialize(name)
|
11
13
|
@name = name
|
12
|
-
validate
|
13
14
|
end
|
14
15
|
|
15
|
-
private
|
16
|
-
|
17
16
|
def validate
|
18
|
-
validate_length(@name,
|
17
|
+
validate_length(@name, NAME_SIZE[:min], NAME_SIZE[:max])
|
19
18
|
end
|
20
19
|
end
|
21
20
|
end
|
@@ -2,11 +2,6 @@ module Codebreaker
|
|
2
2
|
class SecretCodeMatcher
|
3
3
|
EXACT_MATCH_SYMBOL = '+'.freeze
|
4
4
|
DIGIT_MATCH_SYMBOL = '-'.freeze
|
5
|
-
WIN_COMBINATION = '++++'.freeze
|
6
|
-
|
7
|
-
def self.win_combination?(combination)
|
8
|
-
combination == WIN_COMBINATION
|
9
|
-
end
|
10
5
|
|
11
6
|
def initialize(secret_code, guess_code)
|
12
7
|
@secret_code = secret_code.dup
|
@@ -20,20 +15,17 @@ module Codebreaker
|
|
20
15
|
private
|
21
16
|
|
22
17
|
def exact_match
|
23
|
-
result
|
24
|
-
@guess_code.each_with_index do |digit, index|
|
18
|
+
@guess_code.each_with_object('').with_index do |(digit, result), index|
|
25
19
|
next unless digit == @secret_code[index]
|
26
20
|
|
27
21
|
result << EXACT_MATCH_SYMBOL
|
28
22
|
@guess_code[index] = nil
|
29
23
|
@secret_code[index] = nil
|
30
24
|
end
|
31
|
-
result
|
32
25
|
end
|
33
26
|
|
34
27
|
def digit_match
|
35
|
-
|
36
|
-
@guess_code.compact.each do |digit|
|
28
|
+
@guess_code.compact.each_with_object('') do |digit, result|
|
37
29
|
found_index = @secret_code.index(digit)
|
38
30
|
|
39
31
|
next unless found_index
|
@@ -41,7 +33,6 @@ module Codebreaker
|
|
41
33
|
result << DIGIT_MATCH_SYMBOL
|
42
34
|
@secret_code[found_index] = nil
|
43
35
|
end
|
44
|
-
result
|
45
36
|
end
|
46
37
|
end
|
47
38
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Codebreaker
|
2
|
+
class StatsStorage
|
3
|
+
PATH = './db/data.yml'.freeze
|
4
|
+
|
5
|
+
def load
|
6
|
+
File.file?(PATH) ? YAML.load_file(PATH) : []
|
7
|
+
end
|
8
|
+
|
9
|
+
def add(stat)
|
10
|
+
stats = load
|
11
|
+
stats << stat
|
12
|
+
save(stats)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def save(stats)
|
18
|
+
Pathname(PATH).dirname.mkpath
|
19
|
+
File.write(PATH, stats.to_yaml)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/codebreaker/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cb-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dmytro Yakupov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-04-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: i18n
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.8.9
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.8.9
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: fasterer
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -129,23 +143,24 @@ files:
|
|
129
143
|
- bin/console
|
130
144
|
- bin/setup
|
131
145
|
- codebreaker.gemspec
|
146
|
+
- config/i18n.rb
|
147
|
+
- config/locales/en.yml
|
132
148
|
- lib/codebreaker.rb
|
133
|
-
- lib/codebreaker/
|
134
|
-
- lib/codebreaker/
|
135
|
-
- lib/codebreaker/difficulties/hell.rb
|
136
|
-
- lib/codebreaker/difficulties/medium.rb
|
149
|
+
- lib/codebreaker/create_stat_service.rb
|
150
|
+
- lib/codebreaker/difficulty.rb
|
137
151
|
- lib/codebreaker/difficulty_factory.rb
|
138
152
|
- lib/codebreaker/difficulty_type.rb
|
139
153
|
- lib/codebreaker/errors/game_over_error.rb
|
154
|
+
- lib/codebreaker/errors/game_save_error.rb
|
140
155
|
- lib/codebreaker/errors/invalid_difficulty_error.rb
|
141
156
|
- lib/codebreaker/errors/no_hints_error.rb
|
142
157
|
- lib/codebreaker/errors/validation_length_error.rb
|
143
158
|
- lib/codebreaker/game.rb
|
144
|
-
- lib/codebreaker/
|
145
|
-
- lib/codebreaker/mixins/validatable.rb
|
159
|
+
- lib/codebreaker/mixins/validator.rb
|
146
160
|
- lib/codebreaker/player.rb
|
147
161
|
- lib/codebreaker/secret_code_generator.rb
|
148
162
|
- lib/codebreaker/secret_code_matcher.rb
|
163
|
+
- lib/codebreaker/stats_storage.rb
|
149
164
|
- lib/codebreaker/version.rb
|
150
165
|
homepage: https://github.com/YakupovDima/rubygarage-codebreaker-core
|
151
166
|
licenses:
|
data/lib/codebreaker/hint.rb
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
module Codebreaker
|
2
|
-
module Mixins
|
3
|
-
module Validatable
|
4
|
-
def validate
|
5
|
-
raise NotImplementedError
|
6
|
-
end
|
7
|
-
|
8
|
-
def validate_length(value, min, max)
|
9
|
-
return if value.instance_of?(String) && value.size >= min && value.size <= max
|
10
|
-
|
11
|
-
raise Codebreaker::Errors::ValidationLengthError, "invalid length '#{value}'. min=#{min}, max=#{max}"
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|