numeron 1.0.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.
- data/.gitignore +17 -0
- data/Gemfile +15 -0
- data/Guardfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +109 -0
- data/Rakefile +1 -0
- data/lib/numeron.rb +9 -0
- data/lib/numeron/analyzer.rb +140 -0
- data/lib/numeron/calculator.rb +320 -0
- data/lib/numeron/solver.rb +88 -0
- data/lib/numeron/version.rb +3 -0
- data/numeron.gemspec +19 -0
- data/spec/lib/numeron/analyzer_spec.rb +35 -0
- data/spec/lib/numeron/calculator_spec.rb +136 -0
- data/spec/lib/numeron_spec.rb +6 -0
- data/spec/spec_helper.rb +14 -0
- metadata +66 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in numeron.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
group :development, :test do
|
7
|
+
gem 'tapp', :git => 'git://github.com/esminc/tapp.git'
|
8
|
+
gem 'guard'
|
9
|
+
gem 'guard-rspec'
|
10
|
+
gem 'fuubar'
|
11
|
+
gem 'libnotify', :require => false
|
12
|
+
gem 'rb-inotify', :require => false
|
13
|
+
gem 'rb-fsevent', :require => false
|
14
|
+
gem 'growl', :require => false
|
15
|
+
end
|
data/Guardfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 kengos
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
# Numeron
|
2
|
+
|
3
|
+
ヌメロンの解の探索プログラム (Ruby版)
|
4
|
+
|
5
|
+
Androidアプリ ... https://play.google.com/store/apps/details?id=com.jpn.gemstone.numer0n.android
|
6
|
+
|
7
|
+
iPhoneアプリ ... https://itunes.apple.com/jp/app/numer0n-numeron/id512484171?mt=8
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
gem 'numeron'
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install numeron
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
```
|
26
|
+
irb
|
27
|
+
```
|
28
|
+
|
29
|
+
```
|
30
|
+
> require 'numeron'
|
31
|
+
true
|
32
|
+
> Numeron::Solver.new.run
|
33
|
+
Attack number: 123 # <= callした数値を入力
|
34
|
+
Eat number: 0 # <= callした結果 eatの数を入力
|
35
|
+
Bite number: 0 # <= callした結果 biteの数を入力
|
36
|
+
... thinking
|
37
|
+
possibilities: 210 # <= 計算した結果、答えとして可能性がある数値の個数
|
38
|
+
Analyzer Answer: 372 # <= Analyzerがおすすめの答えとして選んだ数
|
39
|
+
Possibilitiy list random: 798 # <= 答えの可能性の一覧からランダムで選んだ数
|
40
|
+
|
41
|
+
finish? [yes|no] # <= yes or yで終了, noで続行
|
42
|
+
```
|
43
|
+
|
44
|
+
## Numeron::Calculator
|
45
|
+
|
46
|
+
ヌメロンの解の可能性として考えられるものを計算する部分。
|
47
|
+
|
48
|
+
|
49
|
+
基本
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
calc = Numeron::Calculator.new
|
53
|
+
calc.input('123', 0, 1) # callした番号, Eatの数, Biteの数
|
54
|
+
p calc.possibilities # 答えの可能性の一覧がでてくる
|
55
|
+
|
56
|
+
# 続いて以下のように245をcallして 2Eat, 0Biteだった場合 11個に絞りこまれる
|
57
|
+
calc.input('245', 2, 0)
|
58
|
+
p calc.possibilities
|
59
|
+
# => ["240", "246", "247", "248", "249", "205", "265", "275", "285", "295", "345"]
|
60
|
+
```
|
61
|
+
|
62
|
+
Shuffle対応(ほとんどテストはしていない)
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
calc = Numeron::Calculator.new
|
66
|
+
calc.input('123', 0, 3)
|
67
|
+
calc.shuffle # シャッフルされた
|
68
|
+
p calc.possibilities.size
|
69
|
+
# => 6
|
70
|
+
|
71
|
+
calc = Numeron::Calculator.new
|
72
|
+
calc.input('123', 1, 1)
|
73
|
+
p calc.possibilities.size
|
74
|
+
# => 42
|
75
|
+
calc.shuffle
|
76
|
+
p calc.possibilities.size
|
77
|
+
# => 126
|
78
|
+
```
|
79
|
+
|
80
|
+
以下の2つの変数を用いている
|
81
|
+
|
82
|
+
`@mays`
|
83
|
+
|
84
|
+
@mays[0]が左側の数値の可能性リスト(初期値0-9)<br>
|
85
|
+
@mays[1]が真ん中の数値の可能性リスト(初期値0-9)<br>
|
86
|
+
@mays[2]が右側の数値の可能性リスト(初期値0-9)<br>
|
87
|
+
|
88
|
+
`@possibilities`
|
89
|
+
|
90
|
+
解の可能性リスト
|
91
|
+
|
92
|
+
## Numeron::Analyzer
|
93
|
+
|
94
|
+
案がなかったので、以下3つの分析方法を適当に実装。<br>
|
95
|
+
計算量はまったく考慮していない
|
96
|
+
|
97
|
+
0e1bを最悪ケースとして、このケースが出た場合に、最も可能性リストが少なくなる数値を計算する機能
|
98
|
+
|
99
|
+
0e1b, 1e0bを最悪ケースとして、これらのケースが出た場合に、最も可能性リストの平均値が少なくなる数値を計算する機能
|
100
|
+
|
101
|
+
可能性リスト中から、最も可能性リストが少なくなる数値を計算する機能
|
102
|
+
|
103
|
+
## Contributing
|
104
|
+
|
105
|
+
1. Fork it
|
106
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
107
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
108
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
109
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/numeron.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Numeron
|
4
|
+
class Analyzer
|
5
|
+
attr_accessor :calculator
|
6
|
+
def initialize(calculator)
|
7
|
+
@calculator = calculator
|
8
|
+
end
|
9
|
+
|
10
|
+
# 次の1手の計算
|
11
|
+
# @param [Symbol] mode :average or :worst
|
12
|
+
# @return [Hash] {recommend: [Array], size: Number}
|
13
|
+
def run(mode = :average, options = {})
|
14
|
+
case mode
|
15
|
+
when :average
|
16
|
+
run_average_mode(options[:cases])
|
17
|
+
when :worst
|
18
|
+
run_worstcase_mode(options[:worst_case])
|
19
|
+
when :possibilities
|
20
|
+
run_possibilities
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# 最悪のケースを0e1b, 1e0bと想定し、それぞれの結果が出た際に可能性の平均値が最も少なくなるケースを推定する
|
25
|
+
def run_average_mode(cases = nil)
|
26
|
+
cases ||= [
|
27
|
+
{eat: 0, bite: 1},
|
28
|
+
{eat: 1, bite: 0}
|
29
|
+
]
|
30
|
+
recommend = []
|
31
|
+
min_size = 10000
|
32
|
+
all_list.each do |f|
|
33
|
+
average = calc_average(f, cases)
|
34
|
+
if min_size == average
|
35
|
+
recommend << f
|
36
|
+
elsif min_size > average
|
37
|
+
recommend = [f]
|
38
|
+
min_size = average
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
{ recommend: recommend, size: min_size }
|
43
|
+
end
|
44
|
+
|
45
|
+
# 最悪のケースを0e1bと推定し、その結果として最も可能性の数が最も少なくなるケースを推定する
|
46
|
+
def run_worstcase_mode(worst_case = {})
|
47
|
+
worst_case = {eat: 0, bite: 1}.merge(worst_case)
|
48
|
+
min_size = 10000
|
49
|
+
recommend = []
|
50
|
+
all_list.each do |f|
|
51
|
+
_calculator = build_calculator
|
52
|
+
_calculator.input(f, worst_case[:eat], worst_case[:bite])
|
53
|
+
if min_size == _calculator.possibilities.size
|
54
|
+
recommend << f
|
55
|
+
elsif min_size > _calculator.possibilities.size
|
56
|
+
recommend = [f]
|
57
|
+
min_size = _calculator.possibilities.size
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
{ recommend: recommend, size: min_size }
|
62
|
+
end
|
63
|
+
|
64
|
+
def run_possibilities
|
65
|
+
possibilities = @calculator.possibilities.clone
|
66
|
+
|
67
|
+
recommend = []
|
68
|
+
min_size = 10000
|
69
|
+
cases = [
|
70
|
+
{eat: 0, bite: 0},
|
71
|
+
{eat: 0, bite: 1},
|
72
|
+
{eat: 0, bite: 2},
|
73
|
+
{eat: 1, bite: 0},
|
74
|
+
{eat: 1, bite: 1},
|
75
|
+
{eat: 2, bite: 0}
|
76
|
+
]
|
77
|
+
possibilities.each do |f|
|
78
|
+
average = calc_average(f, cases, true)
|
79
|
+
if min_size == average
|
80
|
+
recommend << f
|
81
|
+
elsif min_size > average
|
82
|
+
recommend = [f]
|
83
|
+
min_size = average
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
{ recommend: recommend, size: min_size }
|
88
|
+
end
|
89
|
+
|
90
|
+
# attackの結果、推定される結果に対する可能性の数の平均を計算する
|
91
|
+
# @example
|
92
|
+
# [10, 20, 10, 0, 5, 0, 5]の場合は ( 10 + 20 + 10 + 5 + 5 ) / 5 = 10
|
93
|
+
def calc_average(attack, cases, can_zero = false)
|
94
|
+
result = []
|
95
|
+
cases.each do |pattern|
|
96
|
+
_calculator = build_calculator
|
97
|
+
_calculator.input(attack, pattern[:eat], pattern[:bite])
|
98
|
+
result << _calculator.possibilities.size
|
99
|
+
end
|
100
|
+
|
101
|
+
n = 0
|
102
|
+
sum = 0
|
103
|
+
result.each do |f|
|
104
|
+
sum = sum + f
|
105
|
+
n = n + 1 if f > 0
|
106
|
+
end
|
107
|
+
|
108
|
+
if n > 0
|
109
|
+
return sum.to_f / n.to_f
|
110
|
+
elsif can_zero
|
111
|
+
return 0
|
112
|
+
else
|
113
|
+
return 10000
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def build_calculator
|
118
|
+
_calculator = Numeron::Calculator.new
|
119
|
+
_calculator.possibilities = @calculator.possibilities.clone
|
120
|
+
_calculator.mays = @calculator.mays.clone
|
121
|
+
_calculator.histories = @calculator.histories.clone
|
122
|
+
return _calculator
|
123
|
+
end
|
124
|
+
|
125
|
+
def all_list
|
126
|
+
[].tap do |f|
|
127
|
+
(0..9).to_a.each { |i|
|
128
|
+
(0..9).to_a.each { |j|
|
129
|
+
next if i == j
|
130
|
+
(0..9).to_a.each { |k|
|
131
|
+
next if i == k || j == k
|
132
|
+
f << i.to_s + j.to_s + k.to_s
|
133
|
+
}
|
134
|
+
}
|
135
|
+
}
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,320 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Numeron
|
4
|
+
class Calculator
|
5
|
+
attr_accessor :histories, :possibilities, :mays
|
6
|
+
|
7
|
+
def initialize(card_size = 3)
|
8
|
+
@histories = []
|
9
|
+
@mays = [(0..9).to_a, (0..9).to_a, (0..9).to_a]
|
10
|
+
@possibilities = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def input(attack, eat, bite)
|
14
|
+
attack = attack.split(//).map(&:to_i)
|
15
|
+
eat = eat.to_i
|
16
|
+
bite = bite.to_i
|
17
|
+
@histories << {attack: attack, eat: eat, bite: bite}
|
18
|
+
|
19
|
+
if eat == 0 && bite == 0
|
20
|
+
zero_eat_zero_bite(attack)
|
21
|
+
elsif eat == 0 && bite == 3
|
22
|
+
zero_eat_three_bite(attack)
|
23
|
+
elsif eat == 0 && bite == 2
|
24
|
+
zero_eat_two_bite(attack)
|
25
|
+
elsif eat == 0 && bite == 1
|
26
|
+
zero_eat_one_bite(attack)
|
27
|
+
elsif eat == 1 && bite == 2
|
28
|
+
one_eat_two_bite(attack)
|
29
|
+
elsif eat == 1 && bite == 1
|
30
|
+
one_eat_one_bite(attack)
|
31
|
+
elsif eat == 1 && bite == 0
|
32
|
+
one_eat_zero_bite(attack)
|
33
|
+
elsif eat == 2 && bite == 0
|
34
|
+
two_eat_zero_bite(attack)
|
35
|
+
else
|
36
|
+
return false
|
37
|
+
end
|
38
|
+
return true
|
39
|
+
end
|
40
|
+
|
41
|
+
def zero_eat_zero_bite(attack)
|
42
|
+
@mays[0] = @mays[0] - attack
|
43
|
+
@mays[1] = @mays[1] - attack
|
44
|
+
@mays[2] = @mays[2] - attack
|
45
|
+
|
46
|
+
list = []
|
47
|
+
@mays[0].each do |i|
|
48
|
+
@mays[1].each do |j|
|
49
|
+
next if i == j
|
50
|
+
@mays[2].each do |k|
|
51
|
+
next if i == k || j == k
|
52
|
+
list << validation(i, j, k)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
update_possibilities(list)
|
57
|
+
end
|
58
|
+
|
59
|
+
def zero_eat_one_bite(attack)
|
60
|
+
# 各桁のeatの可能性がなくなる
|
61
|
+
@mays[0] = @mays[0] - [attack[0]]
|
62
|
+
@mays[1] = @mays[1] - [attack[1]]
|
63
|
+
@mays[2] = @mays[2] - [attack[2]]
|
64
|
+
|
65
|
+
list = []
|
66
|
+
(@mays[0] - attack).each do |i|
|
67
|
+
(@mays[1] - attack).each do |j|
|
68
|
+
next if i == j
|
69
|
+
list << validation(i, j, attack[0]) if attack[0] != i || attack[0] != j
|
70
|
+
list << validation(i, j, attack[1]) if attack[1] != i || attack[1] != j
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
(@mays[0] - attack).each do |i|
|
75
|
+
(@mays[2] - attack).each do |k|
|
76
|
+
next if i == k
|
77
|
+
list << validation(i, attack[0], k) if attack[0] != i || attack[0] != j
|
78
|
+
list << validation(i, attack[2], k) if attack[1] != i || attack[1] != j
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
(@mays[1] - attack).each do |j|
|
83
|
+
(@mays[2] - attack).each do |k|
|
84
|
+
next if j == k
|
85
|
+
list << validation(attack[1], j, k) if attack[1] != j || attack[1] != k
|
86
|
+
list << validation(attack[2], j, k) if attack[2] != j || attack[2] != k
|
87
|
+
end
|
88
|
+
end
|
89
|
+
update_possibilities(list)
|
90
|
+
end
|
91
|
+
|
92
|
+
def zero_eat_two_bite(attack)
|
93
|
+
@mays[0] = @mays[0] - [attack[0]]
|
94
|
+
@mays[1] = @mays[1] - [attack[1]]
|
95
|
+
@mays[2] = @mays[2] - [attack[2]]
|
96
|
+
list = []
|
97
|
+
|
98
|
+
# 先頭不明
|
99
|
+
(@mays[0] - attack).each do |f|
|
100
|
+
list << validation(f, attack[0], attack[1])
|
101
|
+
list << validation(f, attack[2], attack[0])
|
102
|
+
list << validation(f, attack[2], attack[1])
|
103
|
+
end
|
104
|
+
|
105
|
+
# 中央不明
|
106
|
+
(@mays[1] - attack).each do |f|
|
107
|
+
list << validation(attack[1], f, attack[0])
|
108
|
+
list << validation(attack[2], f, attack[0])
|
109
|
+
list << validation(attack[2], f, attack[1])
|
110
|
+
end
|
111
|
+
|
112
|
+
# 末尾不明
|
113
|
+
(@mays[2] - attack).each do |f|
|
114
|
+
list << validation(attack[1], attack[0], f)
|
115
|
+
list << validation(attack[1], attack[2], f)
|
116
|
+
list << validation(attack[2], attack[0], f)
|
117
|
+
end
|
118
|
+
update_possibilities(list)
|
119
|
+
end
|
120
|
+
|
121
|
+
def zero_eat_three_bite(attack)
|
122
|
+
# 各桁の可能性が2つに絞られる
|
123
|
+
@mays[0] = @mays[0] & [attack[1], attack[2]]
|
124
|
+
@mays[1] = @mays[1] & [attack[0], attack[2]]
|
125
|
+
@mays[2] = @mays[2] & [attack[0], attack[1]]
|
126
|
+
list = [
|
127
|
+
validation(attack[1], attack[2], attack[0]),
|
128
|
+
validation(attack[2], attack[0], attack[1])
|
129
|
+
].compact
|
130
|
+
update_possibilities(list)
|
131
|
+
end
|
132
|
+
|
133
|
+
def one_eat_zero_bite(attack)
|
134
|
+
list = []
|
135
|
+
# 先頭eat
|
136
|
+
(@mays[1] - attack).each do |j|
|
137
|
+
(@mays[2] - attack).each do |k|
|
138
|
+
next if j == k
|
139
|
+
list << validation(attack[0], j, k)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# 真ん中eat
|
144
|
+
(@mays[0] - attack).each do |i|
|
145
|
+
(@mays[2] - attack).each do |k|
|
146
|
+
next if i == k
|
147
|
+
list << validation(i, attack[1], k)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# 末尾eat
|
152
|
+
(@mays[0] - attack).each do |i|
|
153
|
+
(@mays[1] - attack).each do |j|
|
154
|
+
next if i == j
|
155
|
+
list << validation(i, j, attack[2])
|
156
|
+
end
|
157
|
+
end
|
158
|
+
update_possibilities(list)
|
159
|
+
end
|
160
|
+
|
161
|
+
def one_eat_one_bite(attack)
|
162
|
+
list = []
|
163
|
+
(@mays[2] - attack).each do |f|
|
164
|
+
list << validation(attack[0], attack[2], f)
|
165
|
+
end
|
166
|
+
(@mays[1] - attack).each do |f|
|
167
|
+
list << validation(attack[0], f, attack[1])
|
168
|
+
end
|
169
|
+
# 2桁目があっている
|
170
|
+
(@mays[2] - attack).each do |f|
|
171
|
+
list << validation(attack[2], attack[1], f)
|
172
|
+
end
|
173
|
+
(@mays[0] - attack).each do |f|
|
174
|
+
list << validation(f, attack[1], attack[0])
|
175
|
+
end
|
176
|
+
# 3桁目があっている
|
177
|
+
(@mays[0] - attack).each do |f|
|
178
|
+
list << validation(f, attack[0], attack[2])
|
179
|
+
end
|
180
|
+
(@mays[1] - attack).each do |f|
|
181
|
+
list << validation(attack[1], f, attack[2])
|
182
|
+
end
|
183
|
+
update_possibilities(list)
|
184
|
+
end
|
185
|
+
|
186
|
+
def one_eat_two_bite(attack)
|
187
|
+
# attack以外の可能性がなくなるので、積
|
188
|
+
@mays[0] = @mays[0] & attack
|
189
|
+
@mays[1] = @mays[1] & attack
|
190
|
+
@mays[2] = @mays[2] & attack
|
191
|
+
list = [
|
192
|
+
validation(attack[0], attack[2], attack[1]),
|
193
|
+
validation(attack[2], attack[1], attack[0]),
|
194
|
+
validation(attack[1], attack[0], attack[2])
|
195
|
+
].compact
|
196
|
+
update_possibilities(list)
|
197
|
+
end
|
198
|
+
|
199
|
+
def two_eat_zero_bite(attack)
|
200
|
+
# 先頭2つがeat
|
201
|
+
list = []
|
202
|
+
(@mays[2] - attack).each do |k|
|
203
|
+
list << validation(attack[0], attack[1], k)
|
204
|
+
end
|
205
|
+
# 先頭、末尾がeat
|
206
|
+
(@mays[1] - attack).each do |j|
|
207
|
+
list << validation(attack[0], j, attack[2])
|
208
|
+
end
|
209
|
+
# 真ん中、末尾がeat
|
210
|
+
(@mays[0] - attack).each do |i|
|
211
|
+
list << validation(i ,attack[1], attack[2])
|
212
|
+
end
|
213
|
+
update_possibilities(list)
|
214
|
+
end
|
215
|
+
|
216
|
+
def validation(i, j, k)
|
217
|
+
return nil if i == j || i == k || j == k
|
218
|
+
if @mays[0].include?(i) && @mays[1].include?(j) && @mays[2].include?(k)
|
219
|
+
return i.to_s + j.to_s + k.to_s
|
220
|
+
else
|
221
|
+
return nil
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# シャッフル
|
226
|
+
def shuffle
|
227
|
+
@mays[0] = @mays[0] | @mays[1] | @mays[2]
|
228
|
+
@mays[1] = @mays[0] | @mays[1] | @mays[2]
|
229
|
+
@mays[2] = @mays[0] | @mays[1] | @mays[2]
|
230
|
+
list = []
|
231
|
+
@mays[0].each do |i|
|
232
|
+
@mays[1].each do |j|
|
233
|
+
next if i == j
|
234
|
+
@mays[2].each do |k|
|
235
|
+
next if i == k || j == k
|
236
|
+
list << validation(i, j, k)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
@possibilities = list.compact
|
241
|
+
@histories.each do |history|
|
242
|
+
eat_and_bite = history[:eat] + history[:bite]
|
243
|
+
if eat_and_bite == 1
|
244
|
+
one_eat_or_one_bite(history[:attack])
|
245
|
+
elsif eat_and_bite == 2
|
246
|
+
two_eat_or_two_bite(history[:attack])
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def one_eat_or_one_bite(attack)
|
252
|
+
list = []
|
253
|
+
# attack[0] - attack[3]が一桁目に含まれている
|
254
|
+
(@mays[1] - attack).each do |j|
|
255
|
+
(@mays[2] - attack).each do |k|
|
256
|
+
next if j == k
|
257
|
+
list << validation(attack[0], j, k)
|
258
|
+
list << validation(attack[1], j, k)
|
259
|
+
list << validation(attack[2], j, k)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
(@mays[0] - attack).each do |i|
|
263
|
+
# attack[0..3]が2桁目に含まれている
|
264
|
+
(@mays[2] - attack).each do |k|
|
265
|
+
next if i == k
|
266
|
+
list << validation(i, attack[0], k)
|
267
|
+
list << validation(i, attack[1], k)
|
268
|
+
list << validation(i, attack[2], k)
|
269
|
+
end
|
270
|
+
# attack[0..3]が3桁目に含まれている
|
271
|
+
(@mays[1] - attack).each do |j|
|
272
|
+
next if i == j
|
273
|
+
list << validation(i, j, attack[0])
|
274
|
+
list << validation(i, j, attack[1])
|
275
|
+
list << validation(i, j, attack[2])
|
276
|
+
end
|
277
|
+
end
|
278
|
+
update_possibilities(list)
|
279
|
+
end
|
280
|
+
|
281
|
+
def two_eat_or_two_bite(attack)
|
282
|
+
list = []
|
283
|
+
# 末尾が不明
|
284
|
+
(@mays[2] - attack).each do |k|
|
285
|
+
list << validation(attack[0], attack[1], k)
|
286
|
+
list << validation(attack[0], attack[2], k)
|
287
|
+
list << validation(attack[1], attack[0], k)
|
288
|
+
list << validation(attack[1], attack[2], k)
|
289
|
+
list << validation(attack[2], attack[0], k)
|
290
|
+
list << validation(attack[2], attack[1], k)
|
291
|
+
end
|
292
|
+
|
293
|
+
# 真ん中が不明
|
294
|
+
(@mays[1] - attack).each do |j|
|
295
|
+
list << validation(attack[0], j, attack[1])
|
296
|
+
list << validation(attack[0], j, attack[2])
|
297
|
+
list << validation(attack[1], j, attack[0])
|
298
|
+
list << validation(attack[1], j, attack[2])
|
299
|
+
list << validation(attack[2], j, attack[0])
|
300
|
+
list << validation(attack[2], j, attack[1])
|
301
|
+
end
|
302
|
+
|
303
|
+
# 先頭が不明
|
304
|
+
(@mays[0] - attack).each do |i|
|
305
|
+
list << validation(i, attack[0], attack[1])
|
306
|
+
list << validation(i, attack[0], attack[2])
|
307
|
+
list << validation(i, attack[1], attack[0])
|
308
|
+
list << validation(i, attack[1], attack[2])
|
309
|
+
list << validation(i, attack[2], attack[0])
|
310
|
+
list << validation(i, attack[2], attack[1])
|
311
|
+
end
|
312
|
+
update_possibilities(list)
|
313
|
+
end
|
314
|
+
|
315
|
+
def update_possibilities(possibilities)
|
316
|
+
possibilities.compact!
|
317
|
+
@possibilities = @possibilities.nil? ? possibilities : possibilities & @possibilities
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Numeron
|
4
|
+
class Solver
|
5
|
+
attr_accessor :calc, :card_size
|
6
|
+
def initialize
|
7
|
+
@calc = Numeron::Calculator.new
|
8
|
+
@card_size = 3
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
while 1
|
13
|
+
next unless question
|
14
|
+
think
|
15
|
+
finish
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def question
|
20
|
+
attack_number = nil
|
21
|
+
eat = 0
|
22
|
+
bite = 0
|
23
|
+
while 1
|
24
|
+
print "Attack number: "
|
25
|
+
attack_number = STDIN.gets.chomp
|
26
|
+
if @card_size == attack_number.split(//).size
|
27
|
+
break
|
28
|
+
else
|
29
|
+
puts 'Required ' + @card_size.to_s + ' digits.'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
while 1
|
34
|
+
while 1
|
35
|
+
print "Eat number: "
|
36
|
+
eat = STDIN.gets.chomp.to_i
|
37
|
+
if @card_size > eat
|
38
|
+
break
|
39
|
+
else
|
40
|
+
puts 'Required less than ' + @card_size.to_s + ' digits.'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
while 1
|
45
|
+
print "Bite number: "
|
46
|
+
bite = STDIN.gets.chomp.to_i
|
47
|
+
if @card_size >= bite
|
48
|
+
break
|
49
|
+
else
|
50
|
+
print 'Required ' + @card_size.to_s + ' digits or less'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
if eat + bite <= @card_size
|
54
|
+
break
|
55
|
+
else
|
56
|
+
puts "Error, Eat + Bite > " + @card_size.to_s
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
@calc.input(attack_number, eat, bite)
|
61
|
+
end
|
62
|
+
|
63
|
+
def think
|
64
|
+
puts "... thinking"
|
65
|
+
puts "possibilities: " + @calc.possibilities.size.to_s
|
66
|
+
analyzer = Numeron::Analyzer.new(@calc)
|
67
|
+
result = @calc.possibilities.size <= 64 ? analyzer.run(:possibilities) : analyzer.run(:average)
|
68
|
+
if result[:recommend].size > 0
|
69
|
+
puts "Analyzer Answer: " + result[:recommend].sample.to_s
|
70
|
+
else
|
71
|
+
puts "Calculator Error."
|
72
|
+
end
|
73
|
+
puts "Possibilitiy list random: " + @calc.possibilities.sample.to_s
|
74
|
+
end
|
75
|
+
|
76
|
+
def finish
|
77
|
+
while 1
|
78
|
+
print "\nfinish? [yes|no] "
|
79
|
+
f = STDIN.gets.chomp
|
80
|
+
if(f == 'yes' || f == 'y')
|
81
|
+
exit
|
82
|
+
elsif f == 'no' || f == 'n'
|
83
|
+
break
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
data/numeron.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'numeron/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "numeron"
|
8
|
+
gem.version = Numeron::VERSION
|
9
|
+
gem.authors = ["kengos"]
|
10
|
+
gem.email = ["kengo@kengos.jp"]
|
11
|
+
gem.description = %q{numer0nの解を計算します。}
|
12
|
+
gem.summary = %q{numer0n solver}
|
13
|
+
gem.homepage = "https://github.com/kengos/numeron"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Numeron::Analyzer do
|
6
|
+
let(:analyzer) { Numeron::Analyzer.new(calc) }
|
7
|
+
describe '#run_average_mode' do
|
8
|
+
context '0e1b' do
|
9
|
+
let(:calc) { Numeron::Calculator.new.tap{|f| f.input('123', 0, 1) } }
|
10
|
+
it do
|
11
|
+
result = analyzer.run_average_mode
|
12
|
+
result[:recommend].should have(378).items
|
13
|
+
result[:size].should == 60.0
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#run_worstcase_mode' do
|
19
|
+
let(:calc) { Numeron::Calculator.new.tap{|f| f.input('123', 0, 1) } }
|
20
|
+
it do
|
21
|
+
result = analyzer.run_worstcase_mode
|
22
|
+
result[:recommend].should have(21).items
|
23
|
+
result[:size].should == 72
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#run_possibilities' do
|
28
|
+
let(:calc) { Numeron::Calculator.new.tap{|f| f.input('123', 0, 3) } }
|
29
|
+
it do
|
30
|
+
result = analyzer.run_possibilities
|
31
|
+
result[:recommend].should =~ %w(312 231)
|
32
|
+
result[:size].should == 0
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Numeron::Calculator do
|
6
|
+
let(:calc) { Numeron::Calculator.new }
|
7
|
+
describe '#input' do
|
8
|
+
it "0e0b" do
|
9
|
+
calc.input('123', 0, 0)
|
10
|
+
calc.possibilities.should have(7 * 6 * 5).items
|
11
|
+
end
|
12
|
+
|
13
|
+
it "0e1b" do
|
14
|
+
calc.input('123', 0, 1)
|
15
|
+
calc.possibilities.should have(252).items
|
16
|
+
end
|
17
|
+
|
18
|
+
it "0e2b" do
|
19
|
+
calc.input('123', 0, 2)
|
20
|
+
calc.possibilities.should have(63).items
|
21
|
+
calc.input('231', 0, 2)
|
22
|
+
calc.possibilities.should have(21).items
|
23
|
+
end
|
24
|
+
|
25
|
+
it "0e3b" do
|
26
|
+
calc.input('123', 0, 3)
|
27
|
+
calc.possibilities.should =~ %w(231 312)
|
28
|
+
calc.input('231', 0, 3)
|
29
|
+
calc.possibilities.should =~ %w(312)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "1e0b" do
|
33
|
+
calc.input('123', 1, 0)
|
34
|
+
calc.possibilities.should have(126).items
|
35
|
+
end
|
36
|
+
|
37
|
+
it "1e1b" do
|
38
|
+
calc.input('123', 1, 1)
|
39
|
+
calc.possibilities.should have(42).items
|
40
|
+
end
|
41
|
+
|
42
|
+
it "1e2b" do
|
43
|
+
calc.input('123', 1, 2)
|
44
|
+
calc.possibilities.should =~ %w(132 321 213)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "2e0b" do
|
48
|
+
calc.input('123', 2, 0)
|
49
|
+
calc.possibilities.should have(21).items
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#shuffle" do
|
54
|
+
it "0e3b" do
|
55
|
+
calc.input('123', 0, 3)
|
56
|
+
calc.shuffle
|
57
|
+
calc.possibilities.should =~ %w(123 132 213 231 312 321)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "2e0b" do
|
61
|
+
calc.input('123', 2, 0)
|
62
|
+
calc.shuffle
|
63
|
+
# 12?, 13?, 21?, 23?, 31?, 32? が3パターン, ? = [0, 4, 5, 6, 7, 8, 9]
|
64
|
+
# => 6 * 3 * 7= 126
|
65
|
+
calc.possibilities.should have(126).items
|
66
|
+
end
|
67
|
+
|
68
|
+
it "1e1b" do
|
69
|
+
calc.input('123', 1, 1)
|
70
|
+
calc.shuffle
|
71
|
+
calc.possibilities.should have(126).items
|
72
|
+
end
|
73
|
+
|
74
|
+
it "1e0b" do
|
75
|
+
calc.input('123', 1, 0)
|
76
|
+
calc.shuffle
|
77
|
+
calc.possibilities.should have(378).items
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "scenario" do
|
82
|
+
it "1e0b, 1e2b" do
|
83
|
+
calc.input('348', 1, 0)
|
84
|
+
calc.input('123', 1, 2)
|
85
|
+
calc.possibilities.should == ['321']
|
86
|
+
end
|
87
|
+
|
88
|
+
it "0e1b, 0e1b 0e0b 1e0b 1e2b" do
|
89
|
+
calc.input('123', 0, 1)
|
90
|
+
calc.input('210', 0, 1)
|
91
|
+
calc.input('340', 0, 0)
|
92
|
+
calc.input('562', 1, 0)
|
93
|
+
calc.input('892', 1, 2)
|
94
|
+
calc.possibilities.should == %w(982)
|
95
|
+
end
|
96
|
+
|
97
|
+
it "3eat is 571" do
|
98
|
+
calc.input('123', 0, 1)
|
99
|
+
calc.input('245', 0, 1)
|
100
|
+
calc.input('367', 0, 1)
|
101
|
+
calc.input('890', 0, 0)
|
102
|
+
calc.input('416', 0, 1)
|
103
|
+
calc.possibilities.should =~ ['571', '751']
|
104
|
+
end
|
105
|
+
|
106
|
+
it "0e1b, 0e1b" do
|
107
|
+
calc.input('123', 0, 1)
|
108
|
+
calc.input('245', 0, 1)
|
109
|
+
calc.possibilities.should have(75).items
|
110
|
+
end
|
111
|
+
|
112
|
+
it "0e1b, 0e1b 2" do
|
113
|
+
calc.input('123', 0, 1)
|
114
|
+
calc.input('235', 0, 1)
|
115
|
+
calc.possibilities.should have(78).items
|
116
|
+
end
|
117
|
+
|
118
|
+
it "0e1b, 0e1b 3" do
|
119
|
+
calc.input('123', 0, 1)
|
120
|
+
calc.input('145', 0, 1)
|
121
|
+
calc.possibilities.should have(90).items
|
122
|
+
end
|
123
|
+
|
124
|
+
it "0e1b, 0e1b 4" do
|
125
|
+
calc.input('123', 0, 1)
|
126
|
+
calc.input('456', 0, 1)
|
127
|
+
calc.possibilities.should have(96).items
|
128
|
+
end
|
129
|
+
|
130
|
+
it "0e1b, 2e0b" do
|
131
|
+
calc.input('123', 0, 1)
|
132
|
+
calc.input('245', 2, 0)
|
133
|
+
calc.possibilities.should have(11).items
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
begin
|
2
|
+
require 'tapp'
|
3
|
+
rescue Exception
|
4
|
+
end
|
5
|
+
|
6
|
+
require 'rspec'
|
7
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/numeron')
|
8
|
+
|
9
|
+
RSpec.configure do |config|
|
10
|
+
config.mock_with :rspec
|
11
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
12
|
+
config.filter_run focus: true
|
13
|
+
config.run_all_when_everything_filtered = true
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: numeron
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- kengos
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-11-18 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: numer0nの解を計算します。
|
15
|
+
email:
|
16
|
+
- kengo@kengos.jp
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- .gitignore
|
22
|
+
- Gemfile
|
23
|
+
- Guardfile
|
24
|
+
- LICENSE.txt
|
25
|
+
- README.md
|
26
|
+
- Rakefile
|
27
|
+
- lib/numeron.rb
|
28
|
+
- lib/numeron/analyzer.rb
|
29
|
+
- lib/numeron/calculator.rb
|
30
|
+
- lib/numeron/solver.rb
|
31
|
+
- lib/numeron/version.rb
|
32
|
+
- numeron.gemspec
|
33
|
+
- spec/lib/numeron/analyzer_spec.rb
|
34
|
+
- spec/lib/numeron/calculator_spec.rb
|
35
|
+
- spec/lib/numeron_spec.rb
|
36
|
+
- spec/spec_helper.rb
|
37
|
+
homepage: https://github.com/kengos/numeron
|
38
|
+
licenses: []
|
39
|
+
post_install_message:
|
40
|
+
rdoc_options: []
|
41
|
+
require_paths:
|
42
|
+
- lib
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ! '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
requirements: []
|
56
|
+
rubyforge_project:
|
57
|
+
rubygems_version: 1.8.24
|
58
|
+
signing_key:
|
59
|
+
specification_version: 3
|
60
|
+
summary: numer0n solver
|
61
|
+
test_files:
|
62
|
+
- spec/lib/numeron/analyzer_spec.rb
|
63
|
+
- spec/lib/numeron/calculator_spec.rb
|
64
|
+
- spec/lib/numeron_spec.rb
|
65
|
+
- spec/spec_helper.rb
|
66
|
+
has_rdoc:
|