numeron 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|