mgmg 1.3.0 → 1.3.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48ad2b791e601abcf8108fcfceabed64d97b5beeb949e3b409fcb0ef7de148ed
4
- data.tar.gz: 2154c226324c09f202a660bef0b344c3ee57a792dc14e889a9caedb79e5adfb8
3
+ metadata.gz: 5ae250ef7035838c1f0598c419e66259c1cde7ab692e5b5ffefcd78ab845207d
4
+ data.tar.gz: 0fd5f62e8c3ac85ba990010bca631e08111b50d69fad11d782fcadfe075781fd
5
5
  SHA512:
6
- metadata.gz: a40d7b79dd7666a8fc1aa3985bd79f074d80f26ddd28b3c92cdef826edb28406154f3151fe1de5583a4ec3139d6710f71390f7fa60e1c896f2d9452f3dd00b70
7
- data.tar.gz: cf7c8fe93611cad843f0bca7da77f02c24fbe5f7c2b932741f8ce94c80b73df1f355501521b6aabad99a17670d1649f5c7637721974bd096b6eb70726825d0b7
6
+ metadata.gz: 40bdd74efb69a024d87d265340caefd48ee90f4d3fd9d74d2a7a46fd78c6a553bc931798636b1ebc8bed8e95cfa8c6501a554c073897ac0ae9be3fdd37298466
7
+ data.tar.gz: 9c32dfaf9a0d1027afd33e70562e813f7cce5a0378e0e81f806e77e3d4415ee4d779a09b7d3b122d523d9a0f8cc10af1a97da0ee3e3464440777b82efee35c48
@@ -87,3 +87,9 @@
87
87
  - `String#poly`のデフォルト引数を`:cost`に設定.
88
88
  - `Mgmg.#exp`,`String#eff`,`String#peff`,`String#min_levels`を追加.
89
89
  - `String#smith_search`, `String#comp_search`, `String#min_smith`, `String#min_comp`を追加.
90
+
91
+ ## 1.3.1 2020/08/31
92
+ - `String#poly`のキーワード引数`left_associative`が無視される場合があったバグを修正.
93
+ - `Mgmg.#exp`に3引数を与えられるように修正.
94
+ - `String#search`,`Enumerable#search`を追加.
95
+ - `Enumerable#min_levels`,`Enumerable#min_level`,`Enumerable#min_smith`,`Enumerable#min_comp`を追加.
data/README.md CHANGED
@@ -110,12 +110,23 @@ puts '小竜咆哮'.build
110
110
  合成レシピの各鍛冶・防具製作品に対して,レシピ文字列をキー,重量1で作製するために必要な製作Lvを値とした`Hash`を返します.重量1以外は指定できません.
111
111
  最大値は,`self.build.min_level`によって得られます.
112
112
 
113
- ### `String#min_comp(left_associative: true)`
113
+ ### `Enumerable#min_levels(left_associative: true)`
114
+ すべての要素`str`に対する`str.min_levels`をマージした`Hash`を返します.
115
+
116
+ ### `Enumerable#min_level(left_associative: true)`
117
+ `self.min_levels`から武器,防具それぞれに対する最大値を求め,`[必要最小鍛冶Lv, 必要最小防具製作Lv]`を返します.武器,防具の一方のみが含まれる場合,もう一方は`0`になります.
118
+ `String#min_level`と異なり,重量1以外は指定できません.
119
+
120
+ ### `String#min_comp(left_associative: true)`,`Enumerable#min_comp(left_associative: true)`
114
121
  レシピ通りに合成するのに必要な道具製作Lvを返します.ただし,全体が「[]」で囲われているか,非合成レシピの場合,代わりに`0`を返します.
115
122
 
116
- ### `String#min_smith(left_associative: true)`
123
+ `Enumerable`の場合,すべての要素に対する最大値を返します.
124
+
125
+ ### `String#min_smith(left_associative: true)`,`Enumerable#min_smith(left_associative: true)`
117
126
  レシピ通りに製作するのに必要な鍛冶・防具製作Lvを返します.製作物の重量については考慮せず,鍛冶・防具製作に必要な☆条件を満たすために必要な製作Lvを返します.
118
127
 
128
+ `Enumerable`の場合,すべての要素に対し,武器,防具それぞれの最大値を求め,`[必要最小鍛冶Lv, 必要最小防具製作Lv]`を返します.
129
+
119
130
  ### `String#poly(para=:cost, left_associative: true)`
120
131
  レシピ文字列である`self`を解釈し,`para`で指定した9パラ値について,丸めを無視した鍛冶・防具製作Lvと道具製作Lvの2変数からなる多項式関数を示す`Mgmg::TPolynomial`クラスのインスタンスを生成し,返します.`para`は次のシンボルのいずれかを指定します.
121
132
  ```ruby
@@ -131,20 +142,31 @@ puts '小竜咆哮'.build
131
142
 
132
143
  また,`:cost`を渡すことで,消費エレメント量に関する近似多項式を得られます.`self`に`"+"`が含まれていれば合成品とみなし,最後の合成に必要な地エレメント量を,それ以外では,武器なら消費火エレメント量を,防具なら消費水エレメント量を返します.ただし,`self`が既成品そのものの場合,零多項式を返します.
133
144
 
134
- ### `String#smith_seach(para, target, comp, smith_min=nil, smith_max=10000, left_associative: true)`
145
+ ### `String#smith_seach(para, target, comp, smith_min=nil, smith_max=10000, left_associative: true, cut_exp: Float::INFINITY, min_smith: false)`
135
146
  `para`の値が`target`以上となるのに必要な最小の鍛冶・防具製作Lvを二分探索で探索して返します.
136
147
  道具製作Lvは`comp`で固定,鍛冶・防具製作Lvを`smith_min`と`smith_max`で挟み込んで探索します.
137
- `smith_min`が`nil`のとき,最小重量で製作するのに必要な鍛冶・防具製作Lv (`self.build.min_level`)を使用します.
138
- 重量を無視して,製作に必要な最小Lvである`self.min_smith`まで探索したい場合,明示的に指定します.
148
+ `smith_min`が`nil`のとき,`min_smith`が真なら重量を問わず☆的に必要な最小の鍛冶・防具製作Lv (`self.min_smith`),偽なら最小重量で製作するのに必要な鍛冶・防具製作Lv (`self.build.min_level`)を使用します.
139
149
  `smith_min<smith_max`でないとき,`smith_max`で`para`が`target`以上でないときは`ArgumentError`となります.
140
150
  `para`は,`Mgmg::Equip`のメソッド名をシンボルで指定(`:power, :fpower`も可)します.
141
151
  反転などの影響で,探索範囲において`para`の値が(広義)単調増加になっていない場合,正しい結果を返しません.
152
+ `cut_exp`以下の経験値で`target`以上を達成できない場合,`Mgmg::SearchCutException`を発生します.
142
153
 
143
154
  ### `String#comp_search(para, target, smith, comp_min=nil, comp_max=10000, left_associative: true)`
144
155
  `String#smith_seach`とは逆に,鍛冶・防具製作Lvを固定して最小の道具製作Lvを探索します.
145
156
  `comp_min`が`nil`のときは,製作に必要な最小の道具製作Lv (`self.min_comp`)を使用します.
146
157
  その他は`String#smith_seach`と同様です.
147
158
 
159
+ ### `String#search(para, target, smith_min=nil, comp_min=nil, smith_max=10000, comp_max=10000, left_associative: true, step: 1, cut_exp: Float::INFINITY, min_smith: false)`
160
+ `c_min=comp_search(para, target, smith_max, comp_min, comp_max)` から `c_max=comp_search(para, target, smith_max, comp_min, comp_max)` まで,`step`ずつ動かして,
161
+ `smith_search`を行い,その過程で得られた最小経験値の鍛冶・防具製作Lvと道具製作Lvからなる配列を返します.
162
+ レシピ中の,対象パラメータの種別値がすべて奇数,または全て偶数であるなら,`step`を`2`にしても探索すべき範囲を網羅できます.
163
+ `cut_exp`以下の経験値で`target`以上を達成できない場合,`Mgmg::SearchCutException`を発生します.
164
+
165
+ ### `Enumerable#search(para, target, smith_min=nil, armor_min=nil, comp_min=nil, smith_max=10000, armor_max=10000, comp_max=10000, left_associative: true, cut_exp: Float::INFINITY, min_smith: false)`
166
+ 複数装備の組について,`para`の値が`target`以上となる最小経験値の`[鍛冶Lv,防具製作Lv,道具製作Lv]`を返します.
167
+ 武器のみなら防具製作Lvは`0`,防具のみなら鍛冶Lvは`0`,合成なしなら道具製作Lvは`0`となります.
168
+ `cut_exp`以下の経験値で`target`以上を達成できない場合,`Mgmg::SearchCutException`を発生します.
169
+
148
170
  ### `String#eff(para, smith, comp=smith, left_associative: true)`
149
171
  [`smith`を1上げたときの`para`値/(`smith`を1上げるのに必要な経験値), `comp`を1上げたときの`para`値/(`comp`を2上げるのに必要な経験値)]を返します.
150
172
  `para`は,`Mgmg::Equip`のメソッド名をシンボルで指定(`:power, :fpower`も可)します.
@@ -4,6 +4,7 @@ require_relative './mgmg/const'
4
4
  require_relative './mgmg/equip'
5
5
  require_relative './mgmg/poly'
6
6
  require_relative './mgmg/system_equip'
7
+ require_relative './mgmg/search'
7
8
 
8
9
  class String
9
10
  def min_level(w=1)
@@ -22,36 +23,37 @@ class String
22
23
  Mgmg::Equip.build(self, smith, comp, left_associative: left_associative)
23
24
  end
24
25
  def poly(para=:cost, left_associative: true)
26
+ la = left_associative
25
27
  case para
26
28
  when :atkstr
27
- self.poly(:attack) + self.poly(:str)
29
+ self.poly(:attack, left_associative: la) + self.poly(:str, left_associative: la)
28
30
  when :atk_sd
29
- self.poly(:attack) + self.poly(:str).quo(2) + self.poly(:dex).quo(2)
31
+ self.poly(:attack) + self.poly(:str, left_associative: la).quo(2) + self.poly(:dex, left_associative: la).quo(2)
30
32
  when :dex_as
31
- self.poly(:dex) + self.poly(:attack).quo(2) + self.poly(:str).quo(2)
33
+ self.poly(:dex) + self.poly(:attack, left_associative: la).quo(2) + self.poly(:str, left_associative: la).quo(2)
32
34
  when :mag_das
33
- self.poly(:magic) + self.poly(:dex_as).quo(2)
35
+ self.poly(:magic) + self.poly(:dex_as, left_associative: la).quo(2)
34
36
  when :magmag
35
- self.poly(:magdef) + self.poly(:magic).quo(2)
37
+ self.poly(:magdef) + self.poly(:magic, left_associative: la).quo(2)
36
38
  when :cost
37
39
  if Mgmg::SystemEquip.keys.include?(self)
38
40
  return Mgmg::TPolynomial.new(Mgmg::Mat.new(1, 1, 0.quo(1)), 28, 0, 12, 12)
39
41
  end
40
42
  built = self.build(-1)
41
43
  const = (built.star**2) * ( /\+/.match(self) ? 5 : ( built.kind < 8 ? 2 : 1 ) )
42
- ret = poly(:attack) + poly(:phydef) + poly(:magdef)
43
- ret += poly(:hp).quo(4) + poly(:mp).quo(4)
44
- ret += poly(:str) + poly(:dex) + poly(:speed) + poly(:magic)
44
+ ret = poly(:attack, left_associative: la) + poly(:phydef, left_associative: la) + poly(:magdef, left_associative: la)
45
+ ret += poly(:hp, left_associative: la).quo(4) + poly(:mp, left_associative: la).quo(4)
46
+ ret += poly(:str, left_associative: la) + poly(:dex, left_associative: la) + poly(:speed, left_associative: la) + poly(:magic, left_associative: la)
45
47
  ret.mat.body[0][0] += const
46
48
  ret
47
49
  else
48
- Mgmg::TPolynomial.build(self, para, left_associative: left_associative)
50
+ Mgmg::TPolynomial.build(self, para, left_associative: la)
49
51
  end
50
52
  end
51
53
  def eff(para, smith, comp=smith, left_associative: true)
52
- a = build(smith, comp, left_associative: left_associative).method(para).call
53
- b = build(smith+1, comp, left_associative: left_associative).method(para).call
54
- c = build(smith, comp+2, left_associative: left_associative).method(para).call
54
+ a = build(smith, comp, left_associative: left_associative).para_call(para)
55
+ b = build(smith+1, comp, left_associative: left_associative).para_call(para)
56
+ c = build(smith, comp+2, left_associative: left_associative).para_call(para)
55
57
  sden = smith==0 ? 1 : 2*smith-1
56
58
  cden = comp==0 ? 4 : 8*comp
57
59
  [(b-a).quo(sden), (c-a).quo(cden)]
@@ -59,46 +61,6 @@ class String
59
61
  def peff(para, smith, comp=smith, left_associative: true)
60
62
  poly(para, left_associative: left_associative).eff(smith, comp)
61
63
  end
62
- def smith_search(para, target, comp, smith_min=nil, smith_max=10000, left_associative: true)
63
- smith_min = build(-1, -1, left_associative: left_associative).min_level if smith_min.nil?
64
- if smith_max < smith_min
65
- raise ArgumentError, "smith_min <= smith_max is needed, (smith_min, smith_max) = (#{smith_min}, #{smith_max}) are given"
66
- end
67
- if target <= build(smith_min, comp, left_associative: left_associative).method(para).call
68
- return smith_min
69
- elsif build(smith_max, comp, left_associative: left_associative).method(para).call < target
70
- raise ArgumentError, "given smith_max=#{smith_max} does not satisfies the target"
71
- end
72
- while 1 < smith_max - smith_min do
73
- smith = (smith_max - smith_min).div(2) + smith_min
74
- if build(smith, comp, left_associative: left_associative).method(para).call < target
75
- smith_min = smith
76
- else
77
- smith_max = smith
78
- end
79
- end
80
- smith_max
81
- end
82
- def comp_search(para, target, smith, comp_min=nil, comp_max=10000, left_associative: true)
83
- comp_min = min_comp(left_associative: left_associative)
84
- if comp_max < comp_min
85
- raise ArgumentError, "comp_min <= comp_max is needed, (comp_min, comp_max) = (#{comp_min}, #{comp_max}) are given"
86
- end
87
- if target <= build(smith, comp_min, left_associative: left_associative).method(para).call
88
- return comp_min
89
- elsif build(smith, comp_max, left_associative: left_associative).method(para).call < target
90
- raise ArgumentError, "given comp_max=#{comp_max} does not satisfies the target"
91
- end
92
- while 1 < comp_max - comp_min do
93
- comp = (comp_max - comp_min).div(2) + comp_min
94
- if build(smith, comp, left_associative: left_associative).method(para).call < target
95
- comp_min = comp
96
- else
97
- comp_max = comp
98
- end
99
- end
100
- comp_max
101
- end
102
64
  def show(smith=-1, comp=smith, left_associative: true)
103
65
  built = self.build(smith, comp, left_associative: left_associative)
104
66
  pstr = '%.3f' % built.fpower
@@ -120,4 +82,37 @@ module Enumerable
120
82
  end
121
83
  end.sum
122
84
  end
85
+ def min_levels(left_associative: true)
86
+ build(-1, -1, -1, left_associative: left_associative).min_levels
87
+ end
88
+ def min_level(left_associative: true)
89
+ ret = [0, 0]
90
+ build(-1, -1, -1, left_associative: left_associative).min_levels.each do |str, level|
91
+ m = /\A\[*([^\+]+)/.match(str)
92
+ if Mgmg::EquipPosition[m[1].build(0).kind] == 0
93
+ ret[0] = [ret[0], level].max
94
+ else
95
+ ret[1] = [ret[1], level].max
96
+ end
97
+ end
98
+ ret
99
+ end
100
+ def min_smith(left_associative: true)
101
+ ret = [0, 0]
102
+ self.each do |str|
103
+ s = Mgmg::Equip.min_smith(str, left_associative: left_associative)
104
+ m = /\A\[*([^\+]+)/.match(str)
105
+ if Mgmg::EquipPosition[m[1].build(0).kind] == 0
106
+ ret[0] = [ret[0], s].max
107
+ else
108
+ ret[1] = [ret[1], s].max
109
+ end
110
+ end
111
+ ret
112
+ end
113
+ def min_comp(left_associative: true)
114
+ self.map do |str|
115
+ Mgmg::Equip.min_comp(str, left_associative: left_associative)
116
+ end.max
117
+ end
123
118
  end
@@ -72,6 +72,10 @@ module Mgmg
72
72
  end
73
73
  end
74
74
 
75
+ def para_call(para)
76
+ method(para).call
77
+ end
78
+
75
79
  %i|attack phydef magdef hp mp str dex speed magic|.each.with_index do |s, i|
76
80
  define_method(s){ @para[i] }
77
81
  end
@@ -0,0 +1,230 @@
1
+ class String
2
+ def smith_search(para, target, comp, smith_min=nil, smith_max=10000, left_associative: true, cut_exp: Float::INFINITY, min_smith: false)
3
+ if smith_min.nil?
4
+ if min_smith
5
+ smith_min = self.min_smith.min_smith
6
+ else
7
+ smith_min = build(-1, -1, left_associative: left_associative).min_level
8
+ end
9
+ end
10
+ if smith_max < smith_min
11
+ raise ArgumentError, "smith_min <= smith_max is needed, (smith_min, smith_max) = (#{smith_min}, #{smith_max}) are given"
12
+ elsif cut_exp < Float::INFINITY
13
+ begin
14
+ smith_max = [smith_max, Mgmg.invexp2(cut_exp, comp)].min
15
+ rescue
16
+ raise Mgmg::SearchCutException
17
+ end
18
+ end
19
+ if target <= build(smith_min, comp, left_associative: left_associative).para_call(para)
20
+ return smith_min
21
+ elsif build(smith_max, comp, left_associative: left_associative).para_call(para) < target
22
+ raise Mgmg::SearchCutException
23
+ end
24
+ while 1 < smith_max - smith_min do
25
+ smith = (smith_max - smith_min).div(2) + smith_min
26
+ if build(smith, comp, left_associative: left_associative).para_call(para) < target
27
+ smith_min = smith
28
+ else
29
+ smith_max = smith
30
+ end
31
+ end
32
+ smith_max
33
+ end
34
+ def comp_search(para, target, smith, comp_min=nil, comp_max=10000, left_associative: true)
35
+ comp_min = min_comp(left_associative: left_associative)
36
+ if comp_max < comp_min
37
+ raise ArgumentError, "comp_min <= comp_max is needed, (comp_min, comp_max) = (#{comp_min}, #{comp_max}) are given"
38
+ end
39
+ if target <= build(smith, comp_min, left_associative: left_associative).para_call(para)
40
+ return comp_min
41
+ elsif build(smith, comp_max, left_associative: left_associative).para_call(para) < target
42
+ raise Mgmg::SearchCutException
43
+ end
44
+ while 1 < comp_max - comp_min do
45
+ comp = (comp_max - comp_min).div(2) + comp_min
46
+ if build(smith, comp, left_associative: left_associative).para_call(para) < target
47
+ comp_min = comp
48
+ else
49
+ comp_max = comp
50
+ end
51
+ end
52
+ comp_max
53
+ end
54
+ def search(para, target, smith_min=nil, comp_min=nil, smith_max=10000, comp_max=10000, left_associative: true, step: 1, cut_exp: Float::INFINITY, min_smith: false)
55
+ if smith_min.nil?
56
+ if min_smith
57
+ smith_min = self.min_smith
58
+ else
59
+ smith_min = build(-1, -1, left_associative: left_associative).min_level
60
+ end
61
+ end
62
+ comp_min = min_comp(left_associative: left_associative) if comp_min.nil?
63
+ comp_min = comp_search(para, target, smith_max, comp_min, comp_max, left_associative: left_associative)
64
+ smith_max = smith_search(para, target, comp_min, smith_min, smith_max, left_associative: left_associative)
65
+ smith_min = smith_search(para, target, comp_max, smith_min, smith_max, left_associative: left_associative)
66
+ raise Mgmg::SearchCutException if cut_exp < Mgmg.exp(smith_min, comp_min)
67
+ comp_max = comp_search(para, target, smith_min, comp_min, comp_max, left_associative: left_associative)
68
+ minex, ret = Mgmg.exp(smith_min, comp_max), [smith_min, comp_max]
69
+ exp = Mgmg.exp(smith_max, comp_min)
70
+ minex, ret = exp, [smith_max, comp_min] if exp < minex
71
+ (comp_min+step).step(comp_max-1, step) do |comp|
72
+ break if minex < Mgmg.exp(smith_min, comp)
73
+ smith = smith_search(para, target, comp, smith_min, smith_max, left_associative: left_associative, cut_exp: [minex, cut_exp].min)
74
+ exp = Mgmg.exp(smith, comp)
75
+ minex, ret = exp, [smith, comp] if exp < minex
76
+ rescue Mgmg::SearchCutException
77
+ end
78
+ raise Mgmg::SearchCutException, "the result exceeds given cut_exp=#{cut_exp}" if cut_exp < minex
79
+ ret
80
+ end
81
+ end
82
+ module Enumerable
83
+ def smith_search(para, target, armor, comp, smith_min=nil, smith_max=10000, left_associative: true, cut_exp: Float::INFINITY, min_smith: false)
84
+ if smith_min.nil?
85
+ if min_smith
86
+ smith_min = self.min_smith[0]
87
+ else
88
+ smith_min = build(-1, -1, -1, left_associative: left_associative).min_level[0]
89
+ end
90
+ end
91
+ if smith_max < smith_min
92
+ raise ArgumentError, "smith_min <= smith_max is needed, (smith_min, smith_max) = (#{smith_min}, #{smith_max}) are given"
93
+ elsif cut_exp < Float::INFINITY
94
+ begin
95
+ smith_max = [smith_max, Mgmg.invexp3(cut_exp, armor, comp)].min
96
+ rescue
97
+ raise Mgmg::SearchCutException
98
+ end
99
+ end
100
+ if build(smith_max, armor, comp, left_associative: left_associative).para_call(para) < target
101
+ raise Mgmg::SearchCutException
102
+ elsif target <= build(smith_min, armor, comp, left_associative: left_associative).para_call(para)
103
+ return smith_min
104
+ end
105
+ while 1 < smith_max - smith_min do
106
+ smith = (smith_max - smith_min).div(2) + smith_min
107
+ if build(smith, armor, comp, left_associative: left_associative).para_call(para) < target
108
+ smith_min = smith
109
+ else
110
+ smith_max = smith
111
+ end
112
+ end
113
+ smith_max
114
+ end
115
+ def armor_search(para, target, smith, comp, armor_min=nil, armor_max=10000, left_associative: true, cut_exp: Float::INFINITY, min_smith: false)
116
+ if armor_min.nil?
117
+ if min_smith
118
+ armor_min = self.min_smith[1]
119
+ else
120
+ armor_min = build(-1, -1, -1, left_associative: left_associative).min_level[1]
121
+ end
122
+ end
123
+ if armor_max < armor_min
124
+ raise ArgumentError, "armor_min <= armor_max is needed, (armor_min, armor_max) = (#{armor_min}, #{armor_max}) are given"
125
+ elsif cut_exp < Float::INFINITY
126
+ begin
127
+ armor_max = [armor_max, Mgmg.invexp3(cut_exp, smith, comp)].min
128
+ rescue
129
+ raise Mgmg::SearchCutException
130
+ end
131
+ end
132
+ if build(smith, armor_max, comp, left_associative: left_associative).para_call(para) < target
133
+ raise Mgmg::SearchCutException
134
+ elsif target <= build(smith, armor_min, comp, left_associative: left_associative).para_call(para)
135
+ return armor_min
136
+ end
137
+ while 1 < armor_max - armor_min do
138
+ armor = (armor_max - armor_min).div(2) + armor_min
139
+ if build(smith, armor, comp, left_associative: left_associative).para_call(para) < target
140
+ armor_min = armor
141
+ else
142
+ armor_max = armor
143
+ end
144
+ end
145
+ armor_max
146
+ end
147
+ def sa_search(para, target, comp, smith_min=nil, armor_min=nil, smith_max=10000, armor_max=10000, left_associative: true, cut_exp: Float::INFINITY, min_smith: false)
148
+ if min_smith
149
+ s, a = self.min_smith
150
+ else
151
+ s, a = build(-1, -1, -1, left_associative: left_associative).min_level
152
+ end
153
+ smith_min = s if smith_min.nil?
154
+ armor_min = a if armor_min.nil?
155
+ smith_min = smith_search(para, target, armor_max, comp, smith_min, smith_max, left_associative: true)
156
+ armor_min = armor_search(para, target, smith_max, comp, armor_min, armor_max, left_associative: true)
157
+ raise Mgmg::SearchCutException if cut_exp < Mgmg.exp(smith_min, armor_min, comp)
158
+ smith_max = smith_search(para, target, armor_min, comp, smith_min, smith_max, left_associative: true)
159
+ armor_max = armor_search(para, target, smith_min, comp, armor_min, armor_max, left_associative: true)
160
+ minex, ret = Mgmg.exp(smith_min, armor_max, comp), [smith_min, armor_max]
161
+ exp = Mgmg.exp(smith_max, armor_min, comp)
162
+ if exp < minex
163
+ minex, ret = exp, [smith_max, armor_min]
164
+ (armor_min+1).upto(armor_max-1) do |armor|
165
+ break if minex < Mgmg.exp(smith_min, armor, comp)
166
+ smith = smith_search(para, target, armor, comp, smith_min, smith_max, left_associative: left_associative, cut_exp: [minex, cut_exp].min)
167
+ exp = Mgmg.exp(smith, armor, comp)
168
+ minex, ret = exp, [smith, armor] if exp < minex
169
+ rescue Mgmg::SearchCutException
170
+ end
171
+ else
172
+ (smith_min+1).upto(smith_max-1) do |smith|
173
+ break if minex < Mgmg.exp(smith, armor_min, comp)
174
+ armor = armor_search(para, target, smith, comp, armor_min, armor_max, left_associative: left_associative, cut_exp: [minex, cut_exp].min)
175
+ exp = Mgmg.exp(smith, armor, comp)
176
+ minex, ret = exp, [smith, armor] if exp < minex
177
+ rescue Mgmg::SearchCutException
178
+ end
179
+ end
180
+ raise Mgmg::SearchCutException if cut_exp < minex
181
+ ret
182
+ end
183
+ def comp_search(para, target, smith, armor, comp_min=nil, comp_max=10000, left_associative: true)
184
+ comp_min = min_comp(left_associative: left_associative)
185
+ if comp_max < comp_min
186
+ raise ArgumentError, "comp_min <= comp_max is needed, (comp_min, comp_max) = (#{comp_min}, #{comp_max}) are given"
187
+ end
188
+ if target <= build(smith, armor, comp_min, left_associative: left_associative).para_call(para)
189
+ return comp_min
190
+ elsif build(smith, comp_max, left_associative: left_associative).para_call(para) < target
191
+ raise ArgumentError, "given comp_max=#{comp_max} does not satisfies the target"
192
+ end
193
+ while 1 < comp_max - comp_min do
194
+ comp = (comp_max - comp_min).div(2) + comp_min
195
+ if build(smith, armor, comp, left_associative: left_associative).para_call(para) < target
196
+ comp_min = comp
197
+ else
198
+ comp_max = comp
199
+ end
200
+ end
201
+ comp_max
202
+ end
203
+ def search(para, target, smith_min=nil, armor_min=nil, comp_min=nil, smith_max=10000, armor_max=10000, comp_max=10000, left_associative: true, cut_exp: Float::INFINITY, min_smith: false)
204
+ if min_smith
205
+ s, a = self.min_smith
206
+ else
207
+ s, a = build(-1, -1, -1, left_associative: left_associative).min_level
208
+ end
209
+ smith_min = s if smith_min.nil?
210
+ armor_min = a if armor_min.nil?
211
+ comp_min = min_comp(left_associative: left_associative) if comp_min.nil?
212
+ comp_min = comp_search(para, target, smith_max, armor_max, comp_min, comp_max, left_associative: left_associative)
213
+ smith_max, armor_max = sa_search(para, target, comp_min, smith_min, armor_min, smith_max, armor_max, left_associative: left_associative)
214
+ smith_min, armor_min = sa_search(para, target, comp_max, smith_min, armor_min, smith_max, armor_max, left_associative: left_associative)
215
+ raise Mgmg::SearchCutException if cut_exp < Mgmg.exp(smith_min, armor_min, comp_min)
216
+ comp_max = comp_search(para, target, smith_min, armor_min, comp_min, comp_max, left_associative: left_associative)
217
+ minex, ret = Mgmg.exp(smith_min, armor_min, comp_max), [smith_min, armor_min, comp_max]
218
+ exp = Mgmg.exp(smith_max, armor_max, comp_min)
219
+ minex, ret = exp, [smith_max, armor_max, comp_min] if exp < minex
220
+ (comp_min+1).upto(comp_max-1) do |comp|
221
+ break if minex < Mgmg.exp(smith_min, armor_min, comp)
222
+ smith, armor = sa_search(para, target, comp, smith_min, armor_min, smith_max, armor_max, left_associative: left_associative, cut_exp: [minex, cut_exp].min)
223
+ exp = Mgmg.exp(smith, armor, comp)
224
+ minex, ret = exp, [smith, armor, comp] if exp < minex
225
+ rescue Mgmg::SearchCutException
226
+ end
227
+ raise Mgmg::SearchCutException, "the result exceeds given cut_exp=#{cut_exp}" if cut_exp < minex
228
+ ret
229
+ end
230
+ end
@@ -46,9 +46,44 @@ module Mgmg
46
46
  end
47
47
  attr_accessor :equip
48
48
  end
49
+ class SearchCutException < StandardError; end
49
50
 
50
- module_function def exp(smith, comp)
51
- ((smith-1)**2) + (2*((comp-1)**2)) + 3
51
+ module_function def exp(smith, armor, comp=armor.tap{armor=0})
52
+ if armor <= 0
53
+ if smith <= 0
54
+ if comp <= 0
55
+ 0
56
+ else
57
+ (2*((comp-1)**2)) + 2
58
+ end
59
+ else
60
+ if comp <= 0
61
+ ((smith-1)**2) + 1
62
+ else
63
+ ((smith-1)**2) + (2*((comp-1)**2)) + 3
64
+ end
65
+ end
66
+ else
67
+ if smith <= 0
68
+ if comp <= 0
69
+ ((armor-1)**2) + 1
70
+ else
71
+ ((armor-1)**2) + (2*((comp-1)**2)) + 3
72
+ end
73
+ else
74
+ if comp <= 0
75
+ ((smith-1)**2) + ((armor-1)**2) + 2
76
+ else
77
+ ((smith-1)**2) + ((armor-1)**2) + (2*((comp-1)**2)) + 4
78
+ end
79
+ end
80
+ end
81
+ end
82
+ module_function def invexp2(exp, comp)
83
+ Math.sqrt(exp - (2*((comp-1)**2)) - 3).round + 1
84
+ end
85
+ module_function def invexp3(exp, sa, comp)
86
+ Math.sqrt(exp - ((sa-1)**2) - (2*((comp-1)**2)) - 4).round + 1
52
87
  end
53
88
 
54
89
  CharacterList = /[^\(\)\+0123456789\[\]あきくしすたてなねのびりるイウガクグサジスタダチツデトドニノフブペボムラリルロンヴー一万二光兜典刀剣劣匠双古名吹咆品哮地大天太子安宝小帽弓弩当息悪戦手指斧書服木本杖業樹歴殺水氷法火炎牙物玉王産用界異的皮盾短石砕竜紫綿耳聖脛腕腿般良色衣袋覇質軍軽輝輪重量金鉄鎧闇陽靴額飾首骨鬼龍]/
@@ -1,3 +1,3 @@
1
1
  module Mgmg
2
- VERSION = "1.3.0"
2
+ VERSION = "1.3.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mgmg
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - KAZOON
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-08-26 00:00:00.000000000 Z
11
+ date: 2020-08-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -71,6 +71,7 @@ files:
71
71
  - lib/mgmg/const.rb
72
72
  - lib/mgmg/equip.rb
73
73
  - lib/mgmg/poly.rb
74
+ - lib/mgmg/search.rb
74
75
  - lib/mgmg/system_equip.rb
75
76
  - lib/mgmg/utils.rb
76
77
  - lib/mgmg/version.rb