codebreaker-yeroshek 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: '033181db21196b32742be36e1d59d00e85138500'
4
- data.tar.gz: 844353e50606c043ebd8972aa8c738b21d0cf3a5
2
+ SHA256:
3
+ metadata.gz: 6f42c091ab55aba9481d7919b048950bd24267ce442ad0acd6837d6410ad628e
4
+ data.tar.gz: 58dd107ebe7680fb7795b6c90e6907c3b0cf52b2206c8438fca347f2f1cb3dfe
5
5
  SHA512:
6
- metadata.gz: 36c341e9c52d68d08b057520e0f9aa22ca492512858e47e006b4c1ccc08f0b9ad67eaaf79fc99e088e0ed8aee01847230db5b92fc649ca5d37c4d2b31bf4c6b2
7
- data.tar.gz: 466630f29fb74e10abac87bfe7939fc91fd6d9229c0068e9e8872064f8ecfccd0bca9f1adab82f8d386d4e3b417a550f99067554e580f640f4e085249c98b726
6
+ metadata.gz: 3616bc7b2f52acb70fd227c88e0ec9d5ef7ff596cae76d9a1aaf584e3539f447a4c8e14b36c29b1ce203c5c5a8f5610787e1e62607b42eb6c1e8e398eb3ed104
7
+ data.tar.gz: b6aee65a475327123be9a4ba39b843d113ab11e10762f588172075febe12ab546baef39fd42f4f26d3a8470f2356904fab219cb2620e2e28e1ae29d177ed7a80
data/Gemfile CHANGED
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- source "https://rubygems.org"
3
+ source 'https://rubygems.org'
4
4
 
5
- git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
6
 
7
7
  # Specify your gem's dependencies in codebreaker.gemspec
8
8
  gemspec
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- codebreaker (0.1.2)
4
+ codebreaker-yeroshek (0.1.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -71,7 +71,7 @@ PLATFORMS
71
71
 
72
72
  DEPENDENCIES
73
73
  bundler (~> 1.16)
74
- codebreaker!
74
+ codebreaker-yeroshek!
75
75
  fasterer
76
76
  i18n
77
77
  pry
data/Rakefile CHANGED
@@ -1,6 +1,8 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
3
5
 
4
6
  RSpec::Core::RakeTask.new(:spec)
5
7
 
6
- task :default => :spec
8
+ task default: :spec
data/autoload.rb CHANGED
@@ -3,13 +3,13 @@
3
3
  require 'pry'
4
4
  require 'i18n'
5
5
  require 'yaml'
6
- require_relative 'lib/app/i18n_config'
7
- require_relative 'lib/app/modules/validator'
8
- require_relative 'lib/app/entities/game'
9
- require_relative 'lib/app/entities/game'
10
- require_relative 'lib/app/entities/data_storage'
11
- require_relative 'lib/app/entities/processor'
12
- require_relative 'lib/app/entities/menu'
13
- require_relative 'lib/app/entities/renderer'
14
- require_relative 'lib/app/entities/statistics'
6
+ require_relative 'lib/codebreaker/i18n_config'
7
+ require_relative 'lib/codebreaker/modules/validator'
8
+ require_relative 'lib/codebreaker/entities/game'
9
+ require_relative 'lib/codebreaker/entities/game'
10
+ require_relative 'lib/codebreaker/entities/data_storage'
11
+ require_relative 'lib/codebreaker/entities/processor'
12
+ require_relative 'lib/codebreaker/entities/menu'
13
+ require_relative 'lib/codebreaker/entities/renderer'
14
+ require_relative 'lib/codebreaker/entities/statistics'
15
15
  require_relative 'lib/codebreaker/version'
data/bin/console CHANGED
@@ -1,14 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #!/usr/bin/env ruby
2
4
 
3
- require "bundler/setup"
4
- require "codebreaker"
5
+ require 'bundler/setup'
6
+ require 'codebreaker'
5
7
 
6
8
  # You can add fixtures and/or initialization code here to make experimenting
7
9
  # with your gem easier. You can also use a different console, if you like.
8
10
 
9
11
  # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
12
+ # require 'pry'
11
13
  # Pry.start
12
14
 
13
- require "irb"
15
+ require 'irb'
14
16
  IRB.start(__FILE__)
data/bin/setup CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  #!/usr/bin/env bash
2
3
  set -euo pipefail
3
4
  IFS=$'\n\t'
data/codebreaker.gemspec CHANGED
@@ -1,18 +1,19 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  lib = File.expand_path('../lib', __FILE__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'codebreaker/version'
5
6
 
6
7
  Gem::Specification.new do |spec|
7
- spec.name = 'codebreaker-yeroshek'
8
- spec.version = Codebreaker::VERSION
9
- spec.authors = ['Emma Yeroshek']
10
- spec.email = ['emma.yeroshek@gmail.com']
8
+ spec.name = 'codebreaker-yeroshek'
9
+ spec.version = Codebreaker::VERSION
10
+ spec.authors = ['Emma Yeroshek']
11
+ spec.email = ['emma.yeroshek@gmail.com']
11
12
 
12
- spec.summary = 'Codebreaker app'
13
- spec.description = 'Logic game'
14
- spec.homepage = 'https://github.com/mmrshk/rg-codebreaker'
15
- spec.license = 'MIT'
13
+ spec.summary = 'Codebreaker app'
14
+ spec.description = 'Logic game'
15
+ spec.homepage = 'https://github.com/mmrshk/rg-codebreaker'
16
+ spec.license = 'MIT'
16
17
 
17
18
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
19
  # to allow pushing to a single host or delete this section to allow pushing to any host.
@@ -25,21 +26,21 @@ Gem::Specification.new do |spec|
25
26
 
26
27
  # Specify which files should be added to the gem when it is released.
27
28
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
28
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
29
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
29
30
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
30
31
  end
31
- spec.bindir = 'exe'
32
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
32
+ spec.bindir = 'exe'
33
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
33
34
  spec.require_paths = ['lib']
34
35
 
35
36
  spec.add_development_dependency 'bundler', '~> 1.16'
36
- spec.add_development_dependency 'rake', '~> 10.0'
37
- spec.add_development_dependency 'rspec', '~> 3.0'
38
37
  spec.add_development_dependency 'fasterer'
39
38
  spec.add_development_dependency 'i18n'
40
39
  spec.add_development_dependency 'pry'
40
+ spec.add_development_dependency 'rake', '~> 10.0'
41
+ spec.add_development_dependency 'rspec', '~> 3.0'
42
+ spec.add_development_dependency 'rspec_junit_formatter'
41
43
  spec.add_development_dependency 'rubocop'
42
44
  spec.add_development_dependency 'rubocop-rspec'
43
45
  spec.add_development_dependency 'simplecov'
44
- spec.add_development_dependency 'rspec_junit_formatter'
45
46
  end
data/index.rb CHANGED
@@ -2,4 +2,4 @@
2
2
 
3
3
  require_relative 'autoload.rb'
4
4
 
5
- Menu.new.game_menu
5
+ Codebreaker::Entities::Menu.new.game_menu
data/lib/codebreaker.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "codebreaker/version"
3
+ require 'codebreaker/version'
4
4
 
5
5
  module Codebreaker
6
6
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Codebreaker
4
+ module Entities
5
+ class DataStorage
6
+ FILE_NAME = 'database/data.yml'
7
+
8
+ def create
9
+ File.new(FILE_NAME, 'w')
10
+ File.write(FILE_NAME, [].to_yaml)
11
+ end
12
+
13
+ def load
14
+ YAML.load(File.open(FILE_NAME), [Menu]) if storage_exist?
15
+ end
16
+
17
+ def save(object)
18
+ File.open(FILE_NAME, 'w') { |file| file.write(YAML.dump(object)) }
19
+ end
20
+
21
+ def storage_exist?
22
+ File.exist?(FILE_NAME)
23
+ end
24
+
25
+ def save_game_result(object)
26
+ create unless storage_exist?
27
+ save(load.push(object))
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Codebreaker
4
+ module Entities
5
+ class Game
6
+ DIGITS_COUNT = 4
7
+ DIFFICULTIES = {
8
+ easy: {
9
+ attempts: 15,
10
+ hints: 2
11
+ },
12
+ medium: {
13
+ attempts: 10,
14
+ hints: 1
15
+ },
16
+ hell: {
17
+ attempts: 5,
18
+ hints: 1
19
+ }
20
+ }.freeze
21
+ RANGE = (1..6).freeze
22
+
23
+ attr_reader :attempts, :hints, :code
24
+
25
+ def initialize
26
+ @process = Processor.new
27
+ end
28
+
29
+ def generate(difficulty)
30
+ @difficulty = difficulty
31
+ @code = generate_secret_code
32
+ @hints = @code.sample(difficulty[:hints])
33
+ @attempts = difficulty[:attempts]
34
+ end
35
+
36
+ def start_process(command)
37
+ @process.secret_code_proc(code.join, command)
38
+ end
39
+
40
+ def win?(guess)
41
+ code.join == guess
42
+ end
43
+
44
+ def decrease_attempts!
45
+ @attempts -= 1
46
+ end
47
+
48
+ def to_h(name)
49
+ {
50
+ name: name,
51
+ difficulty: DIFFICULTIES.key(@difficulty),
52
+ all_attempts: @difficulty[:attempts],
53
+ all_hints: @difficulty[:hints],
54
+ attempts_used: @difficulty[:attempts] - @attempts,
55
+ hints_used: @difficulty[:hints] - @hints.length
56
+ }
57
+ end
58
+
59
+ def hints_spent?
60
+ hints.empty?
61
+ end
62
+
63
+ def take_a_hint!
64
+ hints.pop
65
+ end
66
+
67
+ private
68
+
69
+ def generate_secret_code
70
+ Array.new(DIGITS_COUNT) { rand(RANGE) }
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Codebreaker
4
+ module Entities
5
+ class Menu
6
+ include Modules::Validator
7
+ attr_reader :storage, :renderer, :game, :guess
8
+
9
+ COMMANDS = {
10
+ start: 'start',
11
+ exit: 'exit',
12
+ rules: 'rules',
13
+ stats: 'stats'
14
+ }.freeze
15
+ CHOOSE_COMMANDS = {
16
+ yes: 'yes'
17
+ }.freeze
18
+ HINT_COMMAND = 'hint'
19
+ MIN_SIZE_VALUE = 3
20
+ MAX_SIZE_VALUE = 20
21
+
22
+ def initialize
23
+ @storage = DataStorage.new
24
+ @renderer = Renderer.new
25
+ @game = Game.new
26
+ @statistics = Statistics.new
27
+ end
28
+
29
+ def game_menu
30
+ renderer.start_message
31
+
32
+ choice_menu_process(ask(:choice_options, commands: COMMANDS.keys.join(' | ')))
33
+ end
34
+
35
+ private
36
+
37
+ def rules
38
+ renderer.rules
39
+ game_menu
40
+ end
41
+
42
+ def start
43
+ @name = registrate_user
44
+ level_choice
45
+ game_process
46
+ end
47
+
48
+ def stats
49
+ scores = storage.load
50
+ render_stats(@statistics.stats(scores)) if scores
51
+ game_menu
52
+ end
53
+
54
+ def ask(phrase_key = nil, options = {})
55
+ renderer.message(phrase_key, options) if phrase_key
56
+ gets.chomp
57
+ end
58
+
59
+ def save_result
60
+ storage.save_game_result(game.to_h(@name)) if ask(:save_results_message) == CHOOSE_COMMANDS[:yes]
61
+ end
62
+
63
+ def registrate_user
64
+ loop do
65
+ name = ask(:registration)
66
+
67
+ return name if name_valid?(name)
68
+
69
+ renderer.registration_name_length_error
70
+ end
71
+ end
72
+
73
+ def name_valid?(name)
74
+ !check_emptyness(name) && check_length(name, MIN_SIZE_VALUE, MAX_SIZE_VALUE)
75
+ end
76
+
77
+ def level_choice
78
+ loop do
79
+ level = ask(:hard_level, levels: Game::DIFFICULTIES.keys.join(' | '))
80
+
81
+ return generate_game(Game::DIFFICULTIES[level.to_sym]) if Game::DIFFICULTIES[level.to_sym]
82
+ return game_menu if level == COMMANDS[:exit]
83
+
84
+ renderer.command_error
85
+ end
86
+ end
87
+
88
+ def generate_game(difficulty)
89
+ game.generate(difficulty)
90
+ renderer.message(:difficulty,
91
+ hints: difficulty[:hints],
92
+ attempts: difficulty[:attempts])
93
+ end
94
+
95
+ def game_process
96
+ while game.attempts.positive?
97
+ @guess = ask
98
+ return handle_win if game.win?(guess)
99
+
100
+ choice_code_process
101
+ end
102
+ handle_lose
103
+ end
104
+
105
+ def choice_code_process
106
+ case guess
107
+ when HINT_COMMAND then hint_process
108
+ when COMMANDS[:exit] then game_menu
109
+ else handle_command
110
+ end
111
+ end
112
+
113
+ def handle_command
114
+ return renderer.command_error unless check_command_range(guess)
115
+
116
+ p game.start_process(guess)
117
+ renderer.round_message
118
+ game.decrease_attempts!
119
+ end
120
+
121
+ def handle_win
122
+ renderer.win_game_message
123
+ save_result
124
+ game_menu
125
+ end
126
+
127
+ def handle_lose
128
+ renderer.lost_game_message(game.code)
129
+ game_menu
130
+ end
131
+
132
+ def hint_process
133
+ return renderer.no_hints_message? if game.hints_spent?
134
+
135
+ renderer.print_hint_number(game.take_a_hint!)
136
+ end
137
+
138
+ def exit_from_game
139
+ renderer.goodbye_message
140
+ exit
141
+ end
142
+
143
+ def choice_menu_process(command_name)
144
+ case command_name
145
+ when COMMANDS[:start] then start
146
+ when COMMANDS[:exit] then exit_from_game
147
+ when COMMANDS[:rules] then rules
148
+ when COMMANDS[:stats] then stats
149
+ else
150
+ renderer.command_error
151
+ game_menu
152
+ end
153
+ end
154
+
155
+ def render_stats(list)
156
+ list.each_with_index do |key, index|
157
+ puts "#{index + 1}: "
158
+ key.each { |param, value| puts "#{param}:#{value}" }
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Codebreaker
4
+ module Entities
5
+ class Processor
6
+ MATCHED_DIGIT_CHAR = '+'
7
+ UNMATCHED_DIGIT_CHAR = '-'
8
+
9
+ attr_reader :guess, :code, :result
10
+
11
+ def secret_code_proc(code, guess)
12
+ @code = code.split('')
13
+ @guess = guess.split('')
14
+ handle_matched_digits.join + handle_matched_digits_with_wrong_position.join
15
+ end
16
+
17
+ private
18
+
19
+ def handle_matched_digits
20
+ code.map.with_index do |_, index|
21
+ next unless code[index] == guess[index]
22
+
23
+ @guess[index], @code[index] = nil
24
+ MATCHED_DIGIT_CHAR
25
+ end
26
+ end
27
+
28
+ def handle_matched_digits_with_wrong_position
29
+ guess.compact.map do |number|
30
+ next unless @code.include?(number)
31
+
32
+ @code.delete_at(code.index(number))
33
+ UNMATCHED_DIGIT_CHAR
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Codebreaker
4
+ module Entities
5
+ class Renderer
6
+ def message(msg_name, hashee = {})
7
+ puts I18n.t(msg_name, hashee)
8
+ end
9
+
10
+ def start_message
11
+ message(:start_message)
12
+ end
13
+
14
+ def rules
15
+ message(:rules)
16
+ end
17
+
18
+ def goodbye_message
19
+ message(:goodbye_message)
20
+ end
21
+
22
+ def save_results_message
23
+ message(:save_results_message)
24
+ end
25
+
26
+ def win_game_message
27
+ message(:win_game_message)
28
+ end
29
+
30
+ def round_message
31
+ message(:round_message)
32
+ end
33
+
34
+ def lost_game_message(code)
35
+ message(:lost_game_message, code: code)
36
+ end
37
+
38
+ def no_hints_message?
39
+ message(:have_no_hints_message)
40
+ end
41
+
42
+ def print_hint_number(code)
43
+ message(:print_hint_number, code: code)
44
+ end
45
+
46
+ def registration_name_emptyness_error
47
+ message(:registration_name_emptyness_error)
48
+ end
49
+
50
+ def registration_name_length_error
51
+ message(:registration_name_length_error)
52
+ end
53
+
54
+ def command_error
55
+ message(:command_error)
56
+ end
57
+
58
+ def command_length_error
59
+ message(:command_length_error)
60
+ end
61
+
62
+ def command_int_error
63
+ message(:command_int_error)
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Codebreaker
4
+ module Entities
5
+ class Statistics
6
+ def stats(list)
7
+ difficulties = list.group_by { |score| score[:difficulty] }
8
+ %i[hell medium easy].reduce([]) do |sorted_difficulties, difficulty_name|
9
+ if difficulties[difficulty_name]
10
+ sorted_difficulties + stats_sort(difficulties[difficulty_name])
11
+ else
12
+ sorted_difficulties
13
+ end
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def stats_sort(scores)
20
+ scores.sort_by! { |score| [score[:attempts_used], score[:hints_used]] }.reverse
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ I18n.load_path << Dir[File.expand_path('lib/codebreaker/locales/') + '/*.yml']
4
+ I18n.config.available_locales = :en
File without changes
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Codebreaker
4
+ module Modules
5
+ module Validator
6
+ VALUE_FORMAT = /^[1-6]{4}$/.freeze
7
+
8
+ def check_emptyness(value)
9
+ value.empty?
10
+ end
11
+
12
+ def check_length(value, min_size, max_size)
13
+ value.size.between?(min_size, max_size)
14
+ end
15
+
16
+ def check_command_range(command)
17
+ command =~ VALUE_FORMAT
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Codebreaker
2
- VERSION = "0.1.1"
4
+ VERSION = "0.1.2"
3
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: codebreaker-yeroshek
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Emma Yeroshek
@@ -25,35 +25,35 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.16'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: fasterer
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '10.0'
40
+ version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rspec
42
+ name: i18n
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '3.0'
47
+ version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '3.0'
54
+ version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: fasterer
56
+ name: pry
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
@@ -67,35 +67,35 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: i18n
70
+ name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ">="
73
+ - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '0'
75
+ version: '10.0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ">="
80
+ - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '0'
82
+ version: '10.0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: pry
84
+ name: rspec
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - ">="
87
+ - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0'
89
+ version: '3.0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - ">="
94
+ - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0'
96
+ version: '3.0'
97
97
  - !ruby/object:Gem::Dependency
98
- name: rubocop
98
+ name: rspec_junit_formatter
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - ">="
@@ -109,7 +109,7 @@ dependencies:
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
111
  - !ruby/object:Gem::Dependency
112
- name: rubocop-rspec
112
+ name: rubocop
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - ">="
@@ -123,7 +123,7 @@ dependencies:
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
125
  - !ruby/object:Gem::Dependency
126
- name: simplecov
126
+ name: rubocop-rspec
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
129
  - - ">="
@@ -137,7 +137,7 @@ dependencies:
137
137
  - !ruby/object:Gem::Version
138
138
  version: '0'
139
139
  - !ruby/object:Gem::Dependency
140
- name: rspec_junit_formatter
140
+ name: simplecov
141
141
  requirement: !ruby/object:Gem::Requirement
142
142
  requirements:
143
143
  - - ">="
@@ -177,16 +177,16 @@ files:
177
177
  - codebreaker.gemspec
178
178
  - database/.gitignore
179
179
  - index.rb
180
- - lib/app/entities/data_storage.rb
181
- - lib/app/entities/game.rb
182
- - lib/app/entities/menu.rb
183
- - lib/app/entities/processor.rb
184
- - lib/app/entities/renderer.rb
185
- - lib/app/entities/statistics.rb
186
- - lib/app/i18n_config.rb
187
- - lib/app/locales/en.yml
188
- - lib/app/modules/validator.rb
189
180
  - lib/codebreaker.rb
181
+ - lib/codebreaker/entities/data_storage.rb
182
+ - lib/codebreaker/entities/game.rb
183
+ - lib/codebreaker/entities/menu.rb
184
+ - lib/codebreaker/entities/processor.rb
185
+ - lib/codebreaker/entities/renderer.rb
186
+ - lib/codebreaker/entities/statistics.rb
187
+ - lib/codebreaker/i18n_config.rb
188
+ - lib/codebreaker/locales/en.yml
189
+ - lib/codebreaker/modules/validator.rb
190
190
  - lib/codebreaker/version.rb
191
191
  homepage: https://github.com/mmrshk/rg-codebreaker
192
192
  licenses:
@@ -209,7 +209,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
209
209
  version: '0'
210
210
  requirements: []
211
211
  rubyforge_project:
212
- rubygems_version: 2.5.2.3
212
+ rubygems_version: 2.7.8
213
213
  signing_key:
214
214
  specification_version: 4
215
215
  summary: Codebreaker app
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class DataStorage
4
- FILE_NAME = 'database/data.yml'
5
-
6
- def create
7
- File.new(FILE_NAME, 'w')
8
- File.write(FILE_NAME, [].to_yaml)
9
- end
10
-
11
- def load
12
- YAML.load(File.open(FILE_NAME), [Menu]) if storage_exist?
13
- end
14
-
15
- def save(object)
16
- File.open(FILE_NAME, 'w') { |file| file.write(YAML.dump(object)) }
17
- end
18
-
19
- def storage_exist?
20
- File.exist?(FILE_NAME)
21
- end
22
-
23
- def save_game_result(object)
24
- create unless storage_exist?
25
- save(load.push(object))
26
- end
27
- end
@@ -1,70 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Game
4
- DIGITS_COUNT = 4
5
- DIFFICULTIES = {
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
- }.freeze
19
- RANGE = (1..6).freeze
20
-
21
- attr_reader :attempts, :hints, :code
22
-
23
- def initialize
24
- @process = Processor.new
25
- end
26
-
27
- def generate(difficulty)
28
- @difficulty = difficulty
29
- @code = generate_secret_code
30
- @hints = @code.sample(difficulty[:hints])
31
- @attempts = difficulty[:attempts]
32
- end
33
-
34
- def start_process(command)
35
- @process.secret_code_proc(code.join, command)
36
- end
37
-
38
- def win?(guess)
39
- code.join == guess
40
- end
41
-
42
- def decrease_attempts!
43
- @attempts -= 1
44
- end
45
-
46
- def to_h(name)
47
- {
48
- name: name,
49
- difficulty: DIFFICULTIES.key(@difficulty),
50
- all_attempts: @difficulty[:attempts],
51
- all_hints: @difficulty[:hints],
52
- attempts_used: @difficulty[:attempts] - @attempts,
53
- hints_used: @difficulty[:hints] - @hints.length
54
- }
55
- end
56
-
57
- def hints_spent?
58
- hints.empty?
59
- end
60
-
61
- def take_a_hint!
62
- hints.pop
63
- end
64
-
65
- private
66
-
67
- def generate_secret_code
68
- Array.new(DIGITS_COUNT) { rand(RANGE) }
69
- end
70
- end
@@ -1,158 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Menu
4
- include Validator
5
- attr_reader :storage, :renderer, :game, :guess
6
-
7
- COMMANDS = {
8
- start: 'start',
9
- exit: 'exit',
10
- rules: 'rules',
11
- stats: 'stats'
12
- }.freeze
13
- CHOOSE_COMMANDS = {
14
- yes: 'yes'
15
- }.freeze
16
- HINT_COMMAND = 'hint'
17
- MIN_SIZE_VALUE = 3
18
- MAX_SIZE_VALUE = 20
19
-
20
- def initialize
21
- @storage = DataStorage.new
22
- @renderer = Renderer.new
23
- @game = Game.new
24
- @statistics = Statistics.new
25
- end
26
-
27
- def game_menu
28
- renderer.start_message
29
- choice_menu_process(ask(:choice_options, commands: COMMANDS.keys.join(' | ')))
30
- end
31
-
32
- private
33
-
34
- def rules
35
- renderer.rules
36
- game_menu
37
- end
38
-
39
- def start
40
- @name = registrate_user
41
- level_choice
42
- game_process
43
- end
44
-
45
- def stats
46
- scores = storage.load
47
- render_stats(@statistics.stats(scores)) if scores
48
- game_menu
49
- end
50
-
51
- def ask(phrase_key = nil, options = {})
52
- renderer.message(phrase_key, options) if phrase_key
53
- gets.chomp
54
- end
55
-
56
- def save_result
57
- storage.save_game_result(game.to_h(@name)) if ask(:save_results_message) == CHOOSE_COMMANDS[:yes]
58
- end
59
-
60
- def registrate_user
61
- loop do
62
- name = ask(:registration)
63
-
64
- return name if name_valid?(name)
65
-
66
- renderer.registration_name_length_error
67
- end
68
- end
69
-
70
- def name_valid?(name)
71
- !check_emptyness(name) && check_length(name, MIN_SIZE_VALUE, MAX_SIZE_VALUE)
72
- end
73
-
74
- def level_choice
75
- loop do
76
- level = ask(:hard_level, levels: Game::DIFFICULTIES.keys.join(' | '))
77
-
78
- return generate_game(Game::DIFFICULTIES[level.to_sym]) if Game::DIFFICULTIES[level.to_sym]
79
- return game_menu if level == COMMANDS[:exit]
80
-
81
- renderer.command_error
82
- end
83
- end
84
-
85
- def generate_game(difficulty)
86
- game.generate(difficulty)
87
- renderer.message(:difficulty,
88
- hints: difficulty[:hints],
89
- attempts: difficulty[:attempts])
90
- end
91
-
92
- def game_process
93
- while game.attempts.positive?
94
- @guess = ask
95
- return handle_win if game.win?(guess)
96
-
97
- choice_code_process
98
- end
99
- handle_lose
100
- end
101
-
102
- def choice_code_process
103
- case guess
104
- when HINT_COMMAND then hint_process
105
- when COMMANDS[:exit] then game_menu
106
- else handle_command
107
- end
108
- end
109
-
110
- def handle_command
111
- return renderer.command_error unless check_command_range(guess)
112
-
113
- p game.start_process(guess)
114
- renderer.round_message
115
- game.decrease_attempts!
116
- end
117
-
118
- def handle_win
119
- renderer.win_game_message
120
- save_result
121
- game_menu
122
- end
123
-
124
- def handle_lose
125
- renderer.lost_game_message(game.code)
126
- game_menu
127
- end
128
-
129
- def hint_process
130
- return renderer.no_hints_message? if game.hints_spent?
131
-
132
- renderer.print_hint_number(game.take_a_hint!)
133
- end
134
-
135
- def exit_from_game
136
- renderer.goodbye_message
137
- exit
138
- end
139
-
140
- def choice_menu_process(command_name)
141
- case command_name
142
- when COMMANDS[:start] then start
143
- when COMMANDS[:exit] then exit_from_game
144
- when COMMANDS[:rules] then rules
145
- when COMMANDS[:stats] then stats
146
- else
147
- renderer.command_error
148
- game_menu
149
- end
150
- end
151
-
152
- def render_stats(list)
153
- list.each_with_index do |key, index|
154
- puts "#{index + 1}: "
155
- key.each { |param, value| puts "#{param}:#{value}" }
156
- end
157
- end
158
- end
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Processor
4
- MATCHED_DIGIT_CHAR = '+'
5
- UNMATCHED_DIGIT_CHAR = '-'
6
-
7
- attr_reader :guess, :code, :result
8
-
9
- def secret_code_proc(code, guess)
10
- @code = code.split('')
11
- @guess = guess.split('')
12
- handle_matched_digits.join + handle_matched_digits_with_wrong_position.join
13
- end
14
-
15
- private
16
-
17
- def handle_matched_digits
18
- code.map.with_index do |_, index|
19
- next unless code[index] == guess[index]
20
-
21
- @guess[index], @code[index] = nil
22
- MATCHED_DIGIT_CHAR
23
- end
24
- end
25
-
26
- def handle_matched_digits_with_wrong_position
27
- guess.compact.map do |number|
28
- next unless @code.include?(number)
29
-
30
- @code.delete_at(code.index(number))
31
- UNMATCHED_DIGIT_CHAR
32
- end
33
- end
34
- end
@@ -1,63 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Renderer
4
- def message(msg_name, hashee = {})
5
- puts I18n.t(msg_name, hashee)
6
- end
7
-
8
- def start_message
9
- message(:start_message)
10
- end
11
-
12
- def rules
13
- message(:rules)
14
- end
15
-
16
- def goodbye_message
17
- message(:goodbye_message)
18
- end
19
-
20
- def save_results_message
21
- message(:save_results_message)
22
- end
23
-
24
- def win_game_message
25
- message(:win_game_message)
26
- end
27
-
28
- def round_message
29
- message(:round_message)
30
- end
31
-
32
- def lost_game_message(code)
33
- message(:lost_game_message, code: code)
34
- end
35
-
36
- def no_hints_message?
37
- message(:have_no_hints_message)
38
- end
39
-
40
- def print_hint_number(code)
41
- message(:print_hint_number, code: code)
42
- end
43
-
44
- def registration_name_emptyness_error
45
- message(:registration_name_emptyness_error)
46
- end
47
-
48
- def registration_name_length_error
49
- message(:registration_name_length_error)
50
- end
51
-
52
- def command_error
53
- message(:command_error)
54
- end
55
-
56
- def command_length_error
57
- message(:command_length_error)
58
- end
59
-
60
- def command_int_error
61
- message(:command_int_error)
62
- end
63
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Statistics
4
- def stats(list)
5
- difficulties = list.group_by { |score| score[:difficulty] }
6
- %w[hell medium easy].reduce([]) do |sorted_difficulties, difficulty_name|
7
- if difficulties[difficulty_name]
8
- sorted_difficulties + stats_sort(difficulties[difficulty_name])
9
- else
10
- sorted_difficulties
11
- end
12
- end
13
- end
14
-
15
- private
16
-
17
- def stats_sort(scores)
18
- scores.sort_by! { |score| [score[:attempts_used], score[:hints_used]] }.reverse
19
- end
20
- end
@@ -1,4 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- I18n.load_path << Dir[File.expand_path('lib/app/locales/') + '/*.yml']
4
- I18n.config.available_locales = :en
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Validator
4
- VALUE_FORMAT = /^[1-6]{4}$/.freeze
5
-
6
- def check_emptyness(value)
7
- value.empty?
8
- end
9
-
10
- def check_length(value, min_size, max_size)
11
- value.size.between?(min_size, max_size)
12
- end
13
-
14
- def check_command_range(command)
15
- command =~ VALUE_FORMAT
16
- end
17
- end