codebreaker_ap 0.1.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 +7 -0
- data/.circleci/config.yml +57 -0
- data/.fasterer.yml +19 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.rubocop.yml +13 -0
- data/.ruby-version +1 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +71 -0
- data/README.md +45 -0
- data/Rakefile +6 -0
- data/bin/console +17 -0
- data/bin/setup +8 -0
- data/codebreaker_ap.gemspec +34 -0
- data/lib/codebreaker_ap.rb +13 -0
- data/lib/codebreaker_ap/entities/base_entity.rb +23 -0
- data/lib/codebreaker_ap/entities/difficulty.rb +55 -0
- data/lib/codebreaker_ap/entities/player.rb +52 -0
- data/lib/codebreaker_ap/game.rb +81 -0
- data/lib/codebreaker_ap/modules/errors.rb +17 -0
- data/lib/codebreaker_ap/modules/statistic.rb +46 -0
- data/lib/codebreaker_ap/modules/storage.rb +13 -0
- data/lib/codebreaker_ap/modules/validation.rb +15 -0
- data/lib/codebreaker_ap/storage_files/locales/en.yml +6 -0
- data/lib/codebreaker_ap/storage_files/locales/ru.yml +6 -0
- data/lib/codebreaker_ap/storage_files/rules/rules_en.yaml +26 -0
- data/lib/codebreaker_ap/storage_files/rules/rules_ru.yaml +25 -0
- data/lib/codebreaker_ap/version.rb +3 -0
- metadata +171 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: deb22669c587e91d496671d56eef7eaa54a719125b85b929437aef5645ce9204
|
|
4
|
+
data.tar.gz: 0121d1d3dfc5e74ec0308522aa6184e467beac87e377a20a58d58f3ab50451dc
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 8499e8a4cdc334447501406f10b18870f633cb0d45a6a89f805a2838e32e525ca514b42f5ca7cf7f0bdd6e90f9f4e591563ba6c75acb8de8abcbc1a0774b42e0
|
|
7
|
+
data.tar.gz: ed5f75a77537b91c6839a60af2e9a776513d5263126f88b0b21ea477b2d3ca9f8184cfa2c67a58fa1228ef913ea3755ea74afd49b45b71921b573646099fbdf8
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
version: 2.1
|
|
2
|
+
|
|
3
|
+
executors:
|
|
4
|
+
default:
|
|
5
|
+
working_directory: ~/repo
|
|
6
|
+
description: The official CircleCI Ruby Docker image
|
|
7
|
+
docker:
|
|
8
|
+
- image: circleci/ruby:2.6.3
|
|
9
|
+
environment:
|
|
10
|
+
ENV: test
|
|
11
|
+
|
|
12
|
+
commands:
|
|
13
|
+
run_linters:
|
|
14
|
+
description: command to start linters
|
|
15
|
+
steps:
|
|
16
|
+
- run:
|
|
17
|
+
name: rubocop
|
|
18
|
+
command: bundle exec rubocop
|
|
19
|
+
- run:
|
|
20
|
+
name: fasterer
|
|
21
|
+
command: bundle exec fasterer
|
|
22
|
+
|
|
23
|
+
run_specs:
|
|
24
|
+
steps:
|
|
25
|
+
- run:
|
|
26
|
+
name: run specs
|
|
27
|
+
command: |
|
|
28
|
+
mkdir /tmp/test-results
|
|
29
|
+
TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)"
|
|
30
|
+
bundle exec rspec --format progress \
|
|
31
|
+
--out /tmp/test-results/rspec.xml \
|
|
32
|
+
$TEST_FILES
|
|
33
|
+
setup_environment:
|
|
34
|
+
steps:
|
|
35
|
+
- checkout
|
|
36
|
+
- run: gem install bundler
|
|
37
|
+
- run: bundle install
|
|
38
|
+
jobs:
|
|
39
|
+
lintering:
|
|
40
|
+
executor: default
|
|
41
|
+
steps:
|
|
42
|
+
- setup_environment
|
|
43
|
+
- run_linters
|
|
44
|
+
run_specs:
|
|
45
|
+
executor: default
|
|
46
|
+
steps:
|
|
47
|
+
- setup_environment
|
|
48
|
+
- run_specs
|
|
49
|
+
|
|
50
|
+
workflows:
|
|
51
|
+
version: 2.1
|
|
52
|
+
build:
|
|
53
|
+
jobs:
|
|
54
|
+
- lintering
|
|
55
|
+
- run_specs:
|
|
56
|
+
requires:
|
|
57
|
+
- lintering
|
data/.fasterer.yml
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
speedups:
|
|
2
|
+
rescue_vs_respond_to: true
|
|
3
|
+
module_eval: true
|
|
4
|
+
shuffle_first_vs_sample: true
|
|
5
|
+
for_loop_vs_each: true
|
|
6
|
+
each_with_index_vs_while: false
|
|
7
|
+
map_flatten_vs_flat_map: true
|
|
8
|
+
reverse_each_vs_reverse_each: true
|
|
9
|
+
select_first_vs_detect: true
|
|
10
|
+
sort_vs_sort_by: true
|
|
11
|
+
fetch_with_argument_vs_block: true
|
|
12
|
+
keys_each_vs_each_key: true
|
|
13
|
+
hash_merge_bang_vs_hash_brackets: true
|
|
14
|
+
block_vs_symbol_to_proc: true
|
|
15
|
+
proc_call_vs_yield: true
|
|
16
|
+
gsub_vs_tr: true
|
|
17
|
+
select_last_vs_reverse_detect: true
|
|
18
|
+
getter_vs_attr_reader: true
|
|
19
|
+
setter_vs_attr_writer: true
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.6.3
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
codebreaker_ap (0.1.0)
|
|
5
|
+
|
|
6
|
+
GEM
|
|
7
|
+
remote: https://rubygems.org/
|
|
8
|
+
specs:
|
|
9
|
+
ast (2.4.0)
|
|
10
|
+
colorize (0.8.1)
|
|
11
|
+
concurrent-ruby (1.1.5)
|
|
12
|
+
diff-lcs (1.3)
|
|
13
|
+
docile (1.3.2)
|
|
14
|
+
fasterer (0.8.1)
|
|
15
|
+
colorize (~> 0.7)
|
|
16
|
+
ruby_parser (>= 3.14.1)
|
|
17
|
+
i18n (1.7.0)
|
|
18
|
+
concurrent-ruby (~> 1.0)
|
|
19
|
+
jaro_winkler (1.5.4)
|
|
20
|
+
json (2.2.0)
|
|
21
|
+
parallel (1.19.1)
|
|
22
|
+
parser (2.6.5.0)
|
|
23
|
+
ast (~> 2.4.0)
|
|
24
|
+
rainbow (3.0.0)
|
|
25
|
+
rake (10.5.0)
|
|
26
|
+
rspec (3.9.0)
|
|
27
|
+
rspec-core (~> 3.9.0)
|
|
28
|
+
rspec-expectations (~> 3.9.0)
|
|
29
|
+
rspec-mocks (~> 3.9.0)
|
|
30
|
+
rspec-core (3.9.0)
|
|
31
|
+
rspec-support (~> 3.9.0)
|
|
32
|
+
rspec-expectations (3.9.0)
|
|
33
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
34
|
+
rspec-support (~> 3.9.0)
|
|
35
|
+
rspec-mocks (3.9.0)
|
|
36
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
37
|
+
rspec-support (~> 3.9.0)
|
|
38
|
+
rspec-support (3.9.0)
|
|
39
|
+
rubocop (0.77.0)
|
|
40
|
+
jaro_winkler (~> 1.5.1)
|
|
41
|
+
parallel (~> 1.10)
|
|
42
|
+
parser (>= 2.6)
|
|
43
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
44
|
+
ruby-progressbar (~> 1.7)
|
|
45
|
+
unicode-display_width (>= 1.4.0, < 1.7)
|
|
46
|
+
ruby-progressbar (1.10.1)
|
|
47
|
+
ruby_parser (3.14.1)
|
|
48
|
+
sexp_processor (~> 4.9)
|
|
49
|
+
sexp_processor (4.13.0)
|
|
50
|
+
simplecov (0.17.1)
|
|
51
|
+
docile (~> 1.1)
|
|
52
|
+
json (>= 1.8, < 3)
|
|
53
|
+
simplecov-html (~> 0.10.0)
|
|
54
|
+
simplecov-html (0.10.2)
|
|
55
|
+
unicode-display_width (1.6.0)
|
|
56
|
+
|
|
57
|
+
PLATFORMS
|
|
58
|
+
ruby
|
|
59
|
+
|
|
60
|
+
DEPENDENCIES
|
|
61
|
+
bundler (~> 2.1.0)
|
|
62
|
+
codebreaker_ap!
|
|
63
|
+
fasterer (~> 0.8.1)
|
|
64
|
+
i18n (~> 1.7.0)
|
|
65
|
+
rake (~> 10.0)
|
|
66
|
+
rspec (~> 3.0)
|
|
67
|
+
rubocop (~> 0.77.0)
|
|
68
|
+
simplecov (~> 0.17.1)
|
|
69
|
+
|
|
70
|
+
BUNDLED WITH
|
|
71
|
+
2.1.0
|
data/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Codebreaker
|
|
2
|
+
|
|
3
|
+
Codebreaker is a logic game in which a code-breaker tries to break a secret code created by a code-maker. The codemaker, which will be played by the application we’re going to write, creates a secret code of four numbers between 1 and 6.
|
|
4
|
+
The codebreaker gets some number of chances to break the code (depends on chosen difficulty). In each turn, the codebreaker makes a guess of 4 numbers. The codemaker then marks the guess with up to 4 signs - + or - or empty spaces.
|
|
5
|
+
- A + indicates an exact match: one of the numbers in the guess is the same as one of the numbers in the secret code and in the same position.
|
|
6
|
+
- A - indicates a number match: one of the numbers in the guess is the same as one of the numbers in the secret code but in a different position.
|
|
7
|
+
- An empty space indicates that there is not a current digit in a secret number.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
Include to Gemfile:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
gem 'game_codebreaker_ap'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
And then execute:
|
|
18
|
+
|
|
19
|
+
$ bundle
|
|
20
|
+
|
|
21
|
+
Or install it yourself as:
|
|
22
|
+
|
|
23
|
+
$ gem install game_codebreaker_ap
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
Create and setup difficulty:
|
|
28
|
+
```ruby
|
|
29
|
+
game = Codebreaker::Game.new
|
|
30
|
+
game.difficulty.initialize_difficulty('hell')
|
|
31
|
+
```
|
|
32
|
+
Print statistic:
|
|
33
|
+
```ruby
|
|
34
|
+
game.show_stats
|
|
35
|
+
```
|
|
36
|
+
Check entered answer and return pluses, minuses or empty spaces:
|
|
37
|
+
```ruby
|
|
38
|
+
game.check_answer('1234')
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Development
|
|
42
|
+
|
|
43
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
44
|
+
|
|
45
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
data/Rakefile
ADDED
data/bin/console
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
require 'codebreaker_ap'
|
|
5
|
+
|
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
8
|
+
|
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
10
|
+
# require "pry"
|
|
11
|
+
# Pry.start
|
|
12
|
+
|
|
13
|
+
require 'irb'
|
|
14
|
+
|
|
15
|
+
game = CodebreakerAp::Game.new
|
|
16
|
+
game.difficulty.initialize_difficulty('hell')
|
|
17
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
lib = File.expand_path("lib", __dir__)
|
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
|
+
require "codebreaker_ap/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "codebreaker_ap"
|
|
7
|
+
spec.version = CodebreakerAp::VERSION
|
|
8
|
+
spec.authors = ["andrewpetrenko1"]
|
|
9
|
+
spec.email = ["andreypetrenko1998@gmail.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = 'Codebreaker game'
|
|
12
|
+
spec.description = 'Codebreaker is a logic game in which a code-breaker tries to break a secret code.'
|
|
13
|
+
spec.homepage = 'https://github.com/andrewpetrenko1/codebreaker_ap'
|
|
14
|
+
spec.license = 'MIT'
|
|
15
|
+
|
|
16
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
17
|
+
|
|
18
|
+
# Specify which files should be added to the gem when it is released.
|
|
19
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
20
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
|
21
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
22
|
+
end
|
|
23
|
+
spec.bindir = "exe"
|
|
24
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
25
|
+
spec.require_paths = ["lib"]
|
|
26
|
+
|
|
27
|
+
spec.add_development_dependency "bundler", "~> 2.1.0"
|
|
28
|
+
spec.add_development_dependency 'fasterer', '~> 0.8.1'
|
|
29
|
+
spec.add_development_dependency 'i18n', '~> 1.7.0'
|
|
30
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
|
31
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
|
32
|
+
spec.add_development_dependency 'rubocop', '~> 0.77.0'
|
|
33
|
+
spec.add_development_dependency 'simplecov', '~> 0.17.1'
|
|
34
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require 'i18n'
|
|
2
|
+
I18n.load_path << Dir[File.expand_path('codebreaker_ap/storage_files/locales', __dir__) + '/*.yml']
|
|
3
|
+
I18n.available_locales = :en, :ru
|
|
4
|
+
require 'yaml'
|
|
5
|
+
require 'codebreaker_ap/modules/storage'
|
|
6
|
+
require 'codebreaker_ap/modules/statistic'
|
|
7
|
+
require 'codebreaker_ap/modules/errors'
|
|
8
|
+
require 'codebreaker_ap/modules/validation'
|
|
9
|
+
require 'codebreaker_ap/entities/base_entity'
|
|
10
|
+
require 'codebreaker_ap/entities/player'
|
|
11
|
+
require 'codebreaker_ap/entities/difficulty'
|
|
12
|
+
require 'codebreaker_ap/game'
|
|
13
|
+
require 'codebreaker_ap/version'
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module CodebreakerAp
|
|
2
|
+
class BaseEntity
|
|
3
|
+
include Validation
|
|
4
|
+
|
|
5
|
+
attr_reader :errors, :validated
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@validated = false
|
|
9
|
+
@errors = []
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def valid?
|
|
13
|
+
@errors.clear
|
|
14
|
+
validate
|
|
15
|
+
@errors.compact!
|
|
16
|
+
@validated = true if @errors.empty?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def validate; end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module CodebreakerAp
|
|
2
|
+
class Difficulty < BaseEntity
|
|
3
|
+
DIFFICULTY = {
|
|
4
|
+
easy: {
|
|
5
|
+
hints: 2,
|
|
6
|
+
attempts: 15
|
|
7
|
+
},
|
|
8
|
+
medium: {
|
|
9
|
+
hints: 1,
|
|
10
|
+
attempts: 10
|
|
11
|
+
},
|
|
12
|
+
hell: {
|
|
13
|
+
hints: 1,
|
|
14
|
+
attempts: 5
|
|
15
|
+
}
|
|
16
|
+
}.freeze
|
|
17
|
+
|
|
18
|
+
attr_reader :level, :hints
|
|
19
|
+
attr_accessor :attempts
|
|
20
|
+
|
|
21
|
+
def initialize
|
|
22
|
+
@level = nil
|
|
23
|
+
@hints = nil
|
|
24
|
+
@attempts = nil
|
|
25
|
+
super()
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def initialize_difficulty(level)
|
|
29
|
+
@level = level
|
|
30
|
+
valid?
|
|
31
|
+
setup_difficulty if validated
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def hint(hints_code)
|
|
35
|
+
return I18n.t(:no_hint) if @hints.zero?
|
|
36
|
+
|
|
37
|
+
@hints -= 1
|
|
38
|
+
hints_code.pop
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def setup_difficulty
|
|
44
|
+
difficulty_options = DIFFICULTY[@level.to_sym]
|
|
45
|
+
@hints = difficulty_options[:hints]
|
|
46
|
+
@attempts = difficulty_options[:attempts]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def validate
|
|
50
|
+
check_difficulty(@level.to_sym, DIFFICULTY.keys)
|
|
51
|
+
rescue WrongDifficultyError => e
|
|
52
|
+
@errors.push(e)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module CodebreakerAp
|
|
2
|
+
class Player < BaseEntity
|
|
3
|
+
attr_reader :name
|
|
4
|
+
attr_accessor :answer, :validated
|
|
5
|
+
|
|
6
|
+
ANSWER_CHARS_RANGE = (1..6).freeze
|
|
7
|
+
ANSWER_LENGTH = (4..4).freeze
|
|
8
|
+
NAME_LENGTH = (3..20).freeze
|
|
9
|
+
def initialize
|
|
10
|
+
@name = nil
|
|
11
|
+
@answer = nil
|
|
12
|
+
super()
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def setup_name(player_name)
|
|
16
|
+
@name = player_name
|
|
17
|
+
valid_instance?
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def setup_answer(answer)
|
|
21
|
+
@answer = answer
|
|
22
|
+
valid_instance?
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def valid_instance?
|
|
28
|
+
valid?
|
|
29
|
+
return if validated
|
|
30
|
+
|
|
31
|
+
puts errors
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def validate
|
|
35
|
+
@errors << validate_name if name
|
|
36
|
+
@errors << validate_answer if answer
|
|
37
|
+
rescue WrongLengthError => e
|
|
38
|
+
@errors.push(e)
|
|
39
|
+
rescue WrongNumbersError => e
|
|
40
|
+
@errors.push(e)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def validate_name
|
|
44
|
+
check_length(@name, NAME_LENGTH)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def validate_answer
|
|
48
|
+
check_length(@answer, ANSWER_LENGTH)
|
|
49
|
+
check_chars_range(@answer, ANSWER_CHARS_RANGE)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
module CodebreakerAp
|
|
2
|
+
class Game
|
|
3
|
+
include Storage
|
|
4
|
+
include Statistic
|
|
5
|
+
|
|
6
|
+
LOCALES_FOLDER = File.expand_path('storage_files', __dir__)
|
|
7
|
+
CODE_LENGTH = 4
|
|
8
|
+
CODE_LENGTH_RANGE = (1..4).freeze
|
|
9
|
+
SECRET_CODE_NUMBERS_RANGE = (1..6).freeze
|
|
10
|
+
IN_POSITION = '+'.freeze
|
|
11
|
+
NOT_IN_POSITION = '-'.freeze
|
|
12
|
+
|
|
13
|
+
attr_reader :secret_code, :difficulty
|
|
14
|
+
|
|
15
|
+
def initialize
|
|
16
|
+
@difficulty = CodebreakerAp::Difficulty.new
|
|
17
|
+
@secret_code = generate_secret_code
|
|
18
|
+
@hints_code = secret_code.to_a.dup.shuffle
|
|
19
|
+
@game_win = false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def check_answer(answer)
|
|
23
|
+
answer = answer.to_s
|
|
24
|
+
|
|
25
|
+
@game_win = true if @secret_code.join == answer
|
|
26
|
+
@difficulty.attempts -= 1
|
|
27
|
+
|
|
28
|
+
compare_answer_with_secret(answer.split(''), secret_code.join.split(''))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def win?
|
|
32
|
+
@game_win
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def take_hint
|
|
36
|
+
difficulty.hint(@hints_code)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def show_rules
|
|
40
|
+
rules = read_from_file("#{LOCALES_FOLDER}/#{I18n.t(:file_rules)}")
|
|
41
|
+
return unless rules
|
|
42
|
+
|
|
43
|
+
puts rules
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def compare_answer_with_secret(answer_array, secret_code_array)
|
|
49
|
+
(position_matches(answer_array, secret_code_array) + other_matches(answer_array, secret_code_array)).join('')
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def position_matches(answer_array, secret_code_array)
|
|
53
|
+
matches = []
|
|
54
|
+
answer_array.each_with_index do |value, index|
|
|
55
|
+
next if value != secret_code_array[index]
|
|
56
|
+
|
|
57
|
+
secret_code_array[index] = nil
|
|
58
|
+
answer_array[index] = nil
|
|
59
|
+
matches.push(IN_POSITION)
|
|
60
|
+
end
|
|
61
|
+
matches
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def other_matches(answer_array, secret_code_array)
|
|
65
|
+
matches = []
|
|
66
|
+
answer_array.each do |value|
|
|
67
|
+
next if value.nil?
|
|
68
|
+
|
|
69
|
+
if secret_code_array.include? value
|
|
70
|
+
secret_code_array[secret_code_array.find_index(value)] = nil
|
|
71
|
+
matches.push(NOT_IN_POSITION)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
matches
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def generate_secret_code
|
|
78
|
+
CODE_LENGTH_RANGE.map { rand(SECRET_CODE_NUMBERS_RANGE) }
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module CodebreakerAp
|
|
2
|
+
class WrongLengthError < StandardError
|
|
3
|
+
def initialize
|
|
4
|
+
super(I18n.t(:wrong_length))
|
|
5
|
+
end
|
|
6
|
+
end
|
|
7
|
+
class WrongDifficultyError < StandardError
|
|
8
|
+
def initialize
|
|
9
|
+
super(I18n.t(:wrong_difficulty))
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
class WrongNumbersError < StandardError
|
|
13
|
+
def initialize
|
|
14
|
+
super(I18n.t(:wrong_chars))
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module CodebreakerAp
|
|
2
|
+
module Statistic
|
|
3
|
+
STATISTIC_FILE = File.expand_path('../storage_files/statistic.yml', __dir__)
|
|
4
|
+
def save_stats(player_name, difficulty)
|
|
5
|
+
save_to_file(create_statistic_data(player_name, difficulty), STATISTIC_FILE)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def show_stats
|
|
9
|
+
statistic = load_statistic
|
|
10
|
+
statistic.each_with_index do |value, index|
|
|
11
|
+
puts statistic(value, index + 1)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def create_statistic_data(player_name, difficulty)
|
|
18
|
+
difficulty_total = Difficulty::DIFFICULTY[difficulty.level.to_sym]
|
|
19
|
+
{
|
|
20
|
+
player: player_name,
|
|
21
|
+
difficulty: difficulty.level,
|
|
22
|
+
total_attempts: difficulty_total[:attempts],
|
|
23
|
+
total_hints: difficulty_total[:hints],
|
|
24
|
+
used_attempts: difficulty_total[:attempts] - difficulty.attempts,
|
|
25
|
+
used_hints: difficulty_total[:hints] - difficulty.hints
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def statistic(value, index)
|
|
30
|
+
"Rating: ##{index}\n"\
|
|
31
|
+
"Player name: #{value[:player]}\n"\
|
|
32
|
+
"Difficulty: #{value[:difficulty]}\n"\
|
|
33
|
+
"Total attempts: #{value[:total_attempts]}\n"\
|
|
34
|
+
"Total hints: #{value[:total_hints]}\n"\
|
|
35
|
+
"Used attempts: #{value[:used_attempts]}\n"\
|
|
36
|
+
"Used hints: #{value[:used_hints]}\n\n"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def load_statistic
|
|
40
|
+
data = YAML.load_stream(File.open(STATISTIC_FILE, 'a+'))
|
|
41
|
+
return unless data
|
|
42
|
+
|
|
43
|
+
data.sort_by { |players| [players[:difficulty], players[:used_attempts]] }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module CodebreakerAp
|
|
2
|
+
module Storage
|
|
3
|
+
def save_to_file(data, file_name)
|
|
4
|
+
File.open(file_name, 'a+') do |this_file|
|
|
5
|
+
this_file.write(YAML.dump(data))
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def read_from_file(file_name)
|
|
10
|
+
YAML.load_file(file_name) if File.exist? file_name
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module CodebreakerAp
|
|
2
|
+
module Validation
|
|
3
|
+
def check_length(value, length)
|
|
4
|
+
raise WrongLengthError unless length.include? value.length
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def check_chars_range(value, range)
|
|
8
|
+
raise WrongNumbersError unless value.chars.all? { |number| range.include? number.to_i }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def check_difficulty(difficulty_name, all_difficulty_names)
|
|
12
|
+
raise WrongDifficultyError unless all_difficulty_names.include?(difficulty_name)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
--- |
|
|
2
|
+
Game Rules:
|
|
3
|
+
- Codebreaker is a logic game in which a code-breaker tries to break a secret code created by a code-maker.
|
|
4
|
+
The codemaker, which will be played by the application we’re going to write, creates a secret code of four numbers between 1 and 6.
|
|
5
|
+
|
|
6
|
+
- The codebreaker gets some number of chances to break the code (depends on chosen difficulty).
|
|
7
|
+
In each turn, the codebreaker makes a guess of 4 numbers. The codemaker then marks the guess with up to 4 signs - + or - or empty spaces.
|
|
8
|
+
|
|
9
|
+
- A '+' indicates an exact match: one of the numbers in the guess is the same as one of the numbers in the secret code and in the same position.
|
|
10
|
+
For example:
|
|
11
|
+
Secret number - 1234
|
|
12
|
+
Input number - 6264
|
|
13
|
+
Number of pluses - 2 (second and fourth position)
|
|
14
|
+
|
|
15
|
+
- A '-' indicates a number match: one of the numbers in the guess is the same as one of the numbers in the secret code but in a different position.
|
|
16
|
+
For example:
|
|
17
|
+
Secret number - 1234
|
|
18
|
+
Input number - 6462
|
|
19
|
+
Number of minuses - 2 (second and fourth position)
|
|
20
|
+
|
|
21
|
+
- An empty space indicates that there is not a current digit in a secret number.
|
|
22
|
+
|
|
23
|
+
- If codebreaker inputs the exact number as a secret number - codebreaker wins the game. If all attempts are spent - codebreaker loses.
|
|
24
|
+
|
|
25
|
+
- Codebreaker also has some number of hints(depends on chosen difficulty). If a user takes a hint - he receives back a separate digit of the secret code.
|
|
26
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
--- |
|
|
2
|
+
Правила игры:
|
|
3
|
+
- Codebreaker - логическая игра, в которой взломщик пытается взломать секретный код, созданный создателем кода.
|
|
4
|
+
Создатель кода, который будет воспроизводиться приложением, которое мы собираемся написать, создает секретный код из четырех чисел от 1 до 6
|
|
5
|
+
|
|
6
|
+
- У нарушителя кода есть некоторое количество шансов нарушить код (зависит от выбранной сложности).
|
|
7
|
+
В каждом ходу взломщик кодов угадывает 4 числа. Затем создатель кода помечает предположение максимум 4 знаками - + или - или пробелами.
|
|
8
|
+
|
|
9
|
+
- «+» указывает на точное совпадение: одно из чисел в предположении совпадает с одним из чисел в секретном коде и находится в той же позиции.
|
|
10
|
+
Например:
|
|
11
|
+
Секретный номер - 1234
|
|
12
|
+
Входной номер - 6264
|
|
13
|
+
Количество плюсов - 2 (вторая и четвертая позиция)
|
|
14
|
+
|
|
15
|
+
- «-» указывает на совпадение чисел: одно из чисел в предположении совпадает с одним из чисел в секретном коде, но в другой позиции.
|
|
16
|
+
Например:
|
|
17
|
+
Секретный номер - 1234
|
|
18
|
+
Входной номер - 6462
|
|
19
|
+
Количество минусов - 2 (вторая и четвертая позиции)
|
|
20
|
+
|
|
21
|
+
- Пустое место указывает на то, что в секретном номере нет текущей цифры.
|
|
22
|
+
|
|
23
|
+
- Если взломщик кода вводит точное число в качестве секретного числа - взломщик кода выигрывает игру. Если все попытки потрачены - взломщик кодов проигрывает.
|
|
24
|
+
|
|
25
|
+
- Codebreaker также имеет некоторое количество подсказок (зависит от выбранной сложности). Если пользователь получает подсказку - он получает обратно отдельную цифру секретного кода.
|
metadata
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: codebreaker_ap
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- andrewpetrenko1
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2019-12-22 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bundler
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 2.1.0
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 2.1.0
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: fasterer
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 0.8.1
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: 0.8.1
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: i18n
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: 1.7.0
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: 1.7.0
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rake
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '10.0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '10.0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rspec
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '3.0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '3.0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: rubocop
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: 0.77.0
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: 0.77.0
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: simplecov
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - "~>"
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: 0.17.1
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - "~>"
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: 0.17.1
|
|
111
|
+
description: Codebreaker is a logic game in which a code-breaker tries to break a
|
|
112
|
+
secret code.
|
|
113
|
+
email:
|
|
114
|
+
- andreypetrenko1998@gmail.com
|
|
115
|
+
executables: []
|
|
116
|
+
extensions: []
|
|
117
|
+
extra_rdoc_files: []
|
|
118
|
+
files:
|
|
119
|
+
- ".circleci/config.yml"
|
|
120
|
+
- ".fasterer.yml"
|
|
121
|
+
- ".gitignore"
|
|
122
|
+
- ".rspec"
|
|
123
|
+
- ".rubocop.yml"
|
|
124
|
+
- ".ruby-version"
|
|
125
|
+
- ".travis.yml"
|
|
126
|
+
- Gemfile
|
|
127
|
+
- Gemfile.lock
|
|
128
|
+
- README.md
|
|
129
|
+
- Rakefile
|
|
130
|
+
- bin/console
|
|
131
|
+
- bin/setup
|
|
132
|
+
- codebreaker_ap.gemspec
|
|
133
|
+
- lib/codebreaker_ap.rb
|
|
134
|
+
- lib/codebreaker_ap/entities/base_entity.rb
|
|
135
|
+
- lib/codebreaker_ap/entities/difficulty.rb
|
|
136
|
+
- lib/codebreaker_ap/entities/player.rb
|
|
137
|
+
- lib/codebreaker_ap/game.rb
|
|
138
|
+
- lib/codebreaker_ap/modules/errors.rb
|
|
139
|
+
- lib/codebreaker_ap/modules/statistic.rb
|
|
140
|
+
- lib/codebreaker_ap/modules/storage.rb
|
|
141
|
+
- lib/codebreaker_ap/modules/validation.rb
|
|
142
|
+
- lib/codebreaker_ap/storage_files/locales/en.yml
|
|
143
|
+
- lib/codebreaker_ap/storage_files/locales/ru.yml
|
|
144
|
+
- lib/codebreaker_ap/storage_files/rules/rules_en.yaml
|
|
145
|
+
- lib/codebreaker_ap/storage_files/rules/rules_ru.yaml
|
|
146
|
+
- lib/codebreaker_ap/version.rb
|
|
147
|
+
homepage: https://github.com/andrewpetrenko1/codebreaker_ap
|
|
148
|
+
licenses:
|
|
149
|
+
- MIT
|
|
150
|
+
metadata:
|
|
151
|
+
homepage_uri: https://github.com/andrewpetrenko1/codebreaker_ap
|
|
152
|
+
post_install_message:
|
|
153
|
+
rdoc_options: []
|
|
154
|
+
require_paths:
|
|
155
|
+
- lib
|
|
156
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
157
|
+
requirements:
|
|
158
|
+
- - ">="
|
|
159
|
+
- !ruby/object:Gem::Version
|
|
160
|
+
version: '0'
|
|
161
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
162
|
+
requirements:
|
|
163
|
+
- - ">="
|
|
164
|
+
- !ruby/object:Gem::Version
|
|
165
|
+
version: '0'
|
|
166
|
+
requirements: []
|
|
167
|
+
rubygems_version: 3.1.2
|
|
168
|
+
signing_key:
|
|
169
|
+
specification_version: 4
|
|
170
|
+
summary: Codebreaker game
|
|
171
|
+
test_files: []
|