cb-core 0.1.6 → 0.1.7
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/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 +7 -8
- 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/game.rb +27 -13
- 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 +22 -10
- data/lib/codebreaker/difficulties/base.rb +0 -17
- data/lib/codebreaker/difficulties/easy.rb +0 -17
- data/lib/codebreaker/difficulties/hell.rb +0 -17
- data/lib/codebreaker/difficulties/medium.rb +0 -17
- data/lib/codebreaker/file_storage.rb +0 -16
- data/lib/codebreaker/hint.rb +0 -18
- data/lib/codebreaker/mixins/validatable.rb +0 -15
- data/lib/codebreaker/stats.rb +0 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fe28b7f9cb8b365bef09a5e66f7e7e4e8bc3613e5fd040582a574374f9fb8e3b
|
4
|
+
data.tar.gz: 9c21dfafcfc866bba9447dd4dfafd40627874e4fd6ad798ffbce0c8c85661036
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e8daed2a5f0e44e76836444198c77059277b4bb040f4cdbc1db51ad24e7353fe70376d8f63c4c1fbd4c74944e6fe460216770c419015417276ba39c8ae9d395b
|
7
|
+
data.tar.gz: e41b6b32135c06ff547ba9dd4f7eea8ccf19490ed2c1249e44760c608b9e77412ad9cae6f90b7c62e24cbcae8fc41b2444cfd41e98321b7b7f869919a20f6a0b
|
data/.fasterer.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.7)
|
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,25 +1,24 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
require 'yaml'
|
3
|
+
require 'i18n'
|
3
4
|
|
5
|
+
require_relative '../config/i18n'
|
6
|
+
|
7
|
+
require_relative 'codebreaker/difficulty'
|
4
8
|
require_relative 'codebreaker/difficulty_factory'
|
5
9
|
require_relative 'codebreaker/difficulty_type'
|
6
|
-
require_relative 'codebreaker/difficulties/base'
|
7
|
-
require_relative 'codebreaker/difficulties/easy'
|
8
|
-
require_relative 'codebreaker/difficulties/medium'
|
9
|
-
require_relative 'codebreaker/difficulties/hell'
|
10
10
|
require_relative 'codebreaker/errors/game_over_error'
|
11
11
|
require_relative 'codebreaker/errors/game_save_error'
|
12
12
|
require_relative 'codebreaker/errors/invalid_difficulty_error'
|
13
13
|
require_relative 'codebreaker/errors/no_hints_error'
|
14
14
|
require_relative 'codebreaker/errors/validation_length_error'
|
15
|
-
require_relative 'codebreaker/
|
15
|
+
require_relative 'codebreaker/create_stat_service'
|
16
16
|
require_relative 'codebreaker/game'
|
17
|
-
require_relative 'codebreaker/
|
18
|
-
require_relative 'codebreaker/mixins/validatable'
|
17
|
+
require_relative 'codebreaker/mixins/validator'
|
19
18
|
require_relative 'codebreaker/player'
|
20
19
|
require_relative 'codebreaker/secret_code_generator'
|
21
20
|
require_relative 'codebreaker/secret_code_matcher'
|
22
|
-
require_relative 'codebreaker/
|
21
|
+
require_relative 'codebreaker/stats_storage'
|
23
22
|
require_relative 'codebreaker/version'
|
24
23
|
|
25
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
|
4
4
|
|
5
5
|
def initialize(player, difficulty_type)
|
6
6
|
@player = player
|
7
|
-
@
|
8
|
-
@difficulty = DifficultyFactory.build(difficulty_type)
|
9
|
-
@hint = Hint.new(@difficulty, @secret_code)
|
7
|
+
@difficulty_type = difficulty_type
|
10
8
|
@attempts_used = 0
|
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
29
|
@attempts_used += 1
|
22
|
-
matching = SecretCodeMatcher.new(
|
23
|
-
check_win(
|
30
|
+
matching = SecretCodeMatcher.new(secret_code, guess).call
|
31
|
+
check_win(guess)
|
24
32
|
matching
|
25
33
|
end
|
26
34
|
|
@@ -37,29 +45,35 @@ module Codebreaker
|
|
37
45
|
end
|
38
46
|
|
39
47
|
def hints_left
|
40
|
-
|
48
|
+
hints.count
|
41
49
|
end
|
42
50
|
|
43
51
|
def attempts_left
|
44
|
-
|
52
|
+
difficulty.attempts - @attempts_used
|
45
53
|
end
|
46
54
|
|
47
55
|
def save_result
|
48
|
-
|
56
|
+
raise Errors::GameSaveError unless win?
|
57
|
+
|
58
|
+
CreateStatService.new(self).call
|
49
59
|
end
|
50
60
|
|
51
61
|
private
|
52
62
|
|
63
|
+
def hints
|
64
|
+
@hints ||= secret_code.sample(difficulty.hints)
|
65
|
+
end
|
66
|
+
|
53
67
|
def no_attempts?
|
54
|
-
|
68
|
+
difficulty.attempts == @attempts_used
|
55
69
|
end
|
56
70
|
|
57
71
|
def ensure_game_is_not_over
|
58
72
|
raise Errors::GameOverError if over?
|
59
73
|
end
|
60
74
|
|
61
|
-
def check_win(
|
62
|
-
@is_win = true if
|
75
|
+
def check_win(guess)
|
76
|
+
@is_win = true if guess == secret_code
|
63
77
|
end
|
64
78
|
end
|
65
79
|
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.7
|
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,11 +143,11 @@ 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
|
@@ -141,14 +155,12 @@ files:
|
|
141
155
|
- lib/codebreaker/errors/invalid_difficulty_error.rb
|
142
156
|
- lib/codebreaker/errors/no_hints_error.rb
|
143
157
|
- lib/codebreaker/errors/validation_length_error.rb
|
144
|
-
- lib/codebreaker/file_storage.rb
|
145
158
|
- lib/codebreaker/game.rb
|
146
|
-
- lib/codebreaker/
|
147
|
-
- lib/codebreaker/mixins/validatable.rb
|
159
|
+
- lib/codebreaker/mixins/validator.rb
|
148
160
|
- lib/codebreaker/player.rb
|
149
161
|
- lib/codebreaker/secret_code_generator.rb
|
150
162
|
- lib/codebreaker/secret_code_matcher.rb
|
151
|
-
- lib/codebreaker/
|
163
|
+
- lib/codebreaker/stats_storage.rb
|
152
164
|
- lib/codebreaker/version.rb
|
153
165
|
homepage: https://github.com/YakupovDima/rubygarage-codebreaker-core
|
154
166
|
licenses:
|
@@ -1,16 +0,0 @@
|
|
1
|
-
module Codebreaker
|
2
|
-
class FileStorage
|
3
|
-
def initialize(path)
|
4
|
-
@path = path
|
5
|
-
end
|
6
|
-
|
7
|
-
def load
|
8
|
-
File.file?(@path) ? YAML.load_file(@path) : nil
|
9
|
-
end
|
10
|
-
|
11
|
-
def save(data)
|
12
|
-
Pathname(@path).dirname.mkpath
|
13
|
-
File.write(@path, data.to_yaml)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
data/lib/codebreaker/hint.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
module Codebreaker
|
2
|
-
class Hint
|
3
|
-
def initialize(difficulty, secret_code)
|
4
|
-
@difficulty = difficulty
|
5
|
-
@hints = secret_code.sample(difficulty.hints)
|
6
|
-
end
|
7
|
-
|
8
|
-
def take
|
9
|
-
raise Errors::NoHintsError if @hints.empty?
|
10
|
-
|
11
|
-
@hints.pop
|
12
|
-
end
|
13
|
-
|
14
|
-
def count
|
15
|
-
@hints.count
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
@@ -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
|
data/lib/codebreaker/stats.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
module Codebreaker
|
2
|
-
class Stats
|
3
|
-
PATH = './db/data.yml'.freeze
|
4
|
-
|
5
|
-
def initialize
|
6
|
-
@storage = FileStorage.new(PATH)
|
7
|
-
end
|
8
|
-
|
9
|
-
def load
|
10
|
-
@storage.load || []
|
11
|
-
end
|
12
|
-
|
13
|
-
def save_game(game)
|
14
|
-
validate_game_is_win(game)
|
15
|
-
data = load
|
16
|
-
data << game_data(game)
|
17
|
-
@storage.save(data)
|
18
|
-
end
|
19
|
-
|
20
|
-
private
|
21
|
-
|
22
|
-
def game_data(game)
|
23
|
-
{
|
24
|
-
name: game.player.name,
|
25
|
-
difficulty: game.difficulty.type,
|
26
|
-
attempts_total: game.difficulty.attempts,
|
27
|
-
attempts_used: game.difficulty.attempts - game.attempts_left,
|
28
|
-
hints_total: game.difficulty.hints,
|
29
|
-
hints_used: game.difficulty.hints - game.hints_left
|
30
|
-
}
|
31
|
-
end
|
32
|
-
|
33
|
-
def validate_game_is_win(game)
|
34
|
-
raise Errors::GameSaveError unless game.win?
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|