mgmg 1.6.1 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/lib/mgmg/ir.rb +126 -12
- data/lib/mgmg/option.rb +11 -7
- data/lib/mgmg/search.rb +366 -77
- data/lib/mgmg/version.rb +1 -1
- data/reference.md +7 -8
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0c14a55ea3bffa94e0b9aa9e65a7a5013d34d8bec62179baae302dabdd150f08
|
4
|
+
data.tar.gz: db35e0738dc57da24e132bacea2cd30a1040d6dc563449ce3b3b8b19a62fa0ae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 475431bc7166ad3c18fb60aec3fb921e43a2987086caf357ab20de75b085da125df70941c4810e06094f3b859c8480258dae41a89b29a46562cb3a4510016aa5
|
7
|
+
data.tar.gz: a4466773b3f3f77e9636b4ab5fa46934ec3781f8d088e889c39dbb72f45e79c5a7217dbb15b79767e067283bf30425449f28804a1794b591bbbef07669ed8749
|
data/CHANGELOG.md
CHANGED
@@ -174,3 +174,13 @@
|
|
174
174
|
- `Mgmg.#find_lowerbound`,`Mgmg.#find_upperbound`において,同値レシピを指定した場合などを例外とするように修正.
|
175
175
|
- `Enumerable#to_recipe`にも`allow_over20`キーワード引数を導入し,デフォルトで☆20を超えるレシピを含む場合に例外とするように修正.
|
176
176
|
- 実効HP(最大HP+腕力)を表す`Mgmg::Equip#hs`を追加.
|
177
|
+
|
178
|
+
## 1.6.2 2022/10/31
|
179
|
+
- 既製品を含むかどうかのチェックを行った場合,同じレシピについては繰り返しチェックしないようにして高速化した.
|
180
|
+
|
181
|
+
## 1.7.0 2022/11/12
|
182
|
+
- オプション`step`を廃止し,レシピに応じて自動的に無駄な探索を省略するように変更.
|
183
|
+
- `String/Enumerable#search/find_max`において,フィボナッチ探索を用いたアルゴリズムに変更.単峰でないため,オプション`comp_ext`を使って追加の探索を行い,取りこぼしを避ける.
|
184
|
+
- `Mgmg.#find_upperbound`のアルゴリズムに誤りがあり,大きく間違えた解を返していた問題を修正.
|
185
|
+
- `Mgmg::IR#attack`等において,引数が`nil`(製作Lvを指定せず,多項式を返す)の場合に,例外となっていたバグを修正.
|
186
|
+
- オブション`smith/armor/comp_max`のデフォルト値を10^9に変更.
|
data/lib/mgmg/ir.rb
CHANGED
@@ -67,9 +67,9 @@ module Mgmg
|
|
67
67
|
ms
|
68
68
|
else
|
69
69
|
if ms == '0'
|
70
|
-
"[[#{ss}[(c+#{@equip9})/2]/100]/#{@den}]"
|
70
|
+
"[[#{ss}[(c+#{@equip9})/2]/100](#{@coef})/#{@den}]"
|
71
71
|
else
|
72
|
-
"#{ms}+[[#{ss}[(c+#{@equip9})/2]/100]/#{@den}]"
|
72
|
+
"#{ms}+[[#{ss}[(c+#{@equip9})/2]/100](#{@coef})/#{@den}]"
|
73
73
|
end
|
74
74
|
end
|
75
75
|
end
|
@@ -136,8 +136,46 @@ module Mgmg
|
|
136
136
|
end
|
137
137
|
end
|
138
138
|
end
|
139
|
-
|
140
|
-
|
139
|
+
class Reined
|
140
|
+
def initialize(body, rein)
|
141
|
+
@body, @rein = body, rein
|
142
|
+
end
|
143
|
+
attr_accessor :body, :rein
|
144
|
+
def initialize_copy(other)
|
145
|
+
@body, @rein = other.body.dup, other.rein.dup
|
146
|
+
end
|
147
|
+
def evaluate(s, c)
|
148
|
+
ret = @body.evaluate(s, c)
|
149
|
+
@rein.each do |r|
|
150
|
+
if r != 0
|
151
|
+
ret *= (100+r).quo(100)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
ret
|
155
|
+
end
|
156
|
+
def evaluate3(s, a, c)
|
157
|
+
ret = @body.evaluate3(s, a, c)
|
158
|
+
@rein.each do |r|
|
159
|
+
if r != 0
|
160
|
+
ret *= (100+r).quo(100)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
ret
|
164
|
+
end
|
165
|
+
def to_s
|
166
|
+
ret = @body.to_s
|
167
|
+
buff = ''
|
168
|
+
@rein.each do |r|
|
169
|
+
if r != 0
|
170
|
+
buff += "(#{100+r}/100)"
|
171
|
+
end
|
172
|
+
end
|
173
|
+
ret = '('+ret+')'+buff unless buff.empty?
|
174
|
+
ret
|
175
|
+
end
|
176
|
+
end
|
177
|
+
def initialize(kind, star, main_m, sub_m, para, eo, rein=[])
|
178
|
+
@kind, @star, @main, @sub, @para, @eo = kind, star, main_m, sub_m, para, eo
|
141
179
|
add_reinforcement(rein)
|
142
180
|
end
|
143
181
|
def add_reinforcement(rein)
|
@@ -150,13 +188,14 @@ module Mgmg
|
|
150
188
|
end
|
151
189
|
self
|
152
190
|
end
|
153
|
-
attr_accessor :kind, :star, :main, :sub, :para, :rein
|
191
|
+
attr_accessor :kind, :star, :main, :sub, :para, :eo, :rein
|
154
192
|
def initialize_copy(other)
|
155
193
|
@kind = other.kind
|
156
194
|
@star = other.star.dup
|
157
195
|
@main = other.main
|
158
196
|
@sub = other.sub
|
159
197
|
@para = other.para.dup
|
198
|
+
@eo = other.eo.dup
|
160
199
|
@rein = other.rein.dup
|
161
200
|
end
|
162
201
|
|
@@ -182,11 +221,41 @@ module Mgmg
|
|
182
221
|
end
|
183
222
|
end
|
184
223
|
|
224
|
+
def eo_para(para)
|
225
|
+
case para
|
226
|
+
when :atkstr
|
227
|
+
self.class.eo_add(eo_para(:attack), eo_para(:str))
|
228
|
+
when :atk_sd
|
229
|
+
self.class.eo_add(self.class.eo_add(eo_para(:attack), eo_para(:str)), eo_para(:dex))
|
230
|
+
when :dex_as
|
231
|
+
self.class.eo_add(self.class.eo_add(eo_para(:dex), eo_para(:attack)), eo_para(:str))
|
232
|
+
when :mag_das
|
233
|
+
self.class.eo_add(eo_para(:magic), eo_para(:dex_as))
|
234
|
+
when :magic2
|
235
|
+
eo_para(:magic)
|
236
|
+
when :magmag
|
237
|
+
self.class.eo_add(eo_para(:magdef), eo_para(:magic))
|
238
|
+
when :pmdef
|
239
|
+
self.class.eo_add(eo_para(:phydef), eo_para(:magmag))
|
240
|
+
when :hs
|
241
|
+
self.class.eo_add(eo_para(:hp), eo_para(:str))
|
242
|
+
when :cost
|
243
|
+
ret = nil
|
244
|
+
@eo.each do |foo|
|
245
|
+
ret = self.class.eo_add(ret, foo)
|
246
|
+
end
|
247
|
+
ret
|
248
|
+
else
|
249
|
+
@eo[%i|attack phydef magdef hp mp str dex speed magic|.index(para)]
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
185
253
|
%i|attack phydef magdef hp mp str dex speed magic|.each.with_index do |sym, i|
|
186
254
|
define_method(sym) do |s=nil, ac=s, x=nil|
|
187
|
-
|
188
|
-
@para[i]
|
189
|
-
|
255
|
+
if s.nil?
|
256
|
+
return Reined.new(@para[i], @rein.map{|r| r.vec[i]})
|
257
|
+
end
|
258
|
+
ret = if x.nil?
|
190
259
|
@para[i].evaluate(s, ac)
|
191
260
|
else
|
192
261
|
@para[i].evaluate3(s, ac, x)
|
@@ -312,12 +381,15 @@ module Mgmg
|
|
312
381
|
@para = Array.new(9) do |i|
|
313
382
|
Multi.sum(self.para[i], other.para[i])
|
314
383
|
end
|
384
|
+
@eo.map!.with_index do |seo, i|
|
385
|
+
self.class.eo_add(seo, other.eo[i])
|
386
|
+
end
|
315
387
|
self
|
316
388
|
end
|
317
389
|
def +(other)
|
318
390
|
self.dup.add!(other)
|
319
391
|
end
|
320
|
-
Zero = self.new(28, Vec.new(6, 0).freeze, 12, 12, Array.new(9){Const.new(0)}.freeze)
|
392
|
+
Zero = self.new(28, Vec.new(6, 0).freeze, 12, 12, Array.new(9){Const.new(0)}.freeze, Array.new(9, nil).freeze)
|
321
393
|
Zero.rein.freeze; Zero.freeze
|
322
394
|
|
323
395
|
class << self
|
@@ -368,7 +440,15 @@ module Mgmg
|
|
368
440
|
end
|
369
441
|
end
|
370
442
|
|
371
|
-
new(
|
443
|
+
eo = Array.new(9) do |i|
|
444
|
+
if EquipFilter[main_k][i] == 0
|
445
|
+
main.eo[i]
|
446
|
+
else
|
447
|
+
eo_add(main.eo[i], eo_mul(2**(Equip9[main_k][i]&1), sub.eo[i]))
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
new(main_k, main_s+sub_s, main_sub, sub_main, para, eo)
|
372
452
|
end
|
373
453
|
def smith(str)
|
374
454
|
str = Mgmg.check_string(str)
|
@@ -395,7 +475,15 @@ module Mgmg
|
|
395
475
|
end
|
396
476
|
end
|
397
477
|
|
398
|
-
|
478
|
+
eo = Array.new(9) do |i|
|
479
|
+
if coef[i] == 0
|
480
|
+
nil
|
481
|
+
else
|
482
|
+
0
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
ret = new(kind, (main_s+sub_s).div(2), main_mc, sub_mc, para, eo)
|
399
487
|
Cache.store(str, ret.freeze)
|
400
488
|
ret.dup
|
401
489
|
end
|
@@ -403,7 +491,33 @@ module Mgmg
|
|
403
491
|
para = equip.para.map do |value|
|
404
492
|
Mgmg::IR::Const.new(value)
|
405
493
|
end
|
406
|
-
|
494
|
+
eo = equip.para.map do |value|
|
495
|
+
if value == 0
|
496
|
+
nil
|
497
|
+
else
|
498
|
+
0
|
499
|
+
end
|
500
|
+
end
|
501
|
+
new(equip.kind, equip.star, equip.main, equip.sub, para, eo)
|
502
|
+
end
|
503
|
+
|
504
|
+
def eo_add(eo0, eo1)
|
505
|
+
if eo0
|
506
|
+
if eo1
|
507
|
+
eo0 | eo1
|
508
|
+
else
|
509
|
+
eo0
|
510
|
+
end
|
511
|
+
else
|
512
|
+
eo1
|
513
|
+
end
|
514
|
+
end
|
515
|
+
def eo_mul(eq, seo)
|
516
|
+
if seo
|
517
|
+
eq | seo
|
518
|
+
else
|
519
|
+
nil
|
520
|
+
end
|
407
521
|
end
|
408
522
|
end
|
409
523
|
end
|
data/lib/mgmg/option.rb
CHANGED
@@ -2,12 +2,14 @@ module Mgmg
|
|
2
2
|
class Option
|
3
3
|
Defaults = {
|
4
4
|
left_associative: true, include_system_equips: true,
|
5
|
-
smith_max:
|
5
|
+
smith_max: 1_000_000_000, armor_max: 1_000_000_000, comp_max: 1_000_000_000,
|
6
|
+
comp_ext: [0.1, 100, 10_000, 0.1]
|
6
7
|
}
|
7
8
|
def initialize(
|
8
9
|
left_associative: Defaults[:left_associative],
|
9
10
|
smith_min: nil, armor_min:nil, comp_min: nil, smith_max: Defaults[:smith_max], armor_max: Defaults[:armor_max], comp_max: Defaults[:comp_max],
|
10
|
-
|
11
|
+
comp_ext: Defaults[:comp_ext],
|
12
|
+
magdef_maximize: true,
|
11
13
|
target_weight: 0, reinforcement: [], buff: nil,
|
12
14
|
irep: nil, cut_exp: Float::INFINITY,
|
13
15
|
include_system_equips: Defaults[:include_system_equips]
|
@@ -19,7 +21,7 @@ module Mgmg
|
|
19
21
|
@smith_max = smith_max
|
20
22
|
@armor_max = armor_max
|
21
23
|
@comp_max = comp_max
|
22
|
-
@
|
24
|
+
@comp_ext = comp_ext
|
23
25
|
@magdef_maximize = magdef_maximize
|
24
26
|
@target_weight = target_weight
|
25
27
|
@reinforcement = reinforcement
|
@@ -33,9 +35,10 @@ module Mgmg
|
|
33
35
|
@irep = irep
|
34
36
|
@cut_exp = cut_exp
|
35
37
|
@include_system_equips = include_system_equips
|
38
|
+
@system_equips_checked = false
|
36
39
|
end
|
37
|
-
attr_accessor :left_associative, :smith_min, :armor_min, :comp_min, :smith_max, :armor_max, :comp_max
|
38
|
-
attr_accessor :
|
40
|
+
attr_accessor :left_associative, :smith_min, :armor_min, :comp_min, :smith_max, :armor_max, :comp_max, :comp_ext
|
41
|
+
attr_accessor :magdef_maximize, :target_weight, :reinforcement, :irep, :cut_exp, :include_system_equips, :system_equips_checked
|
39
42
|
def initialize_copy(other)
|
40
43
|
@left_associative = other.left_associative
|
41
44
|
@smith_min = other.smith_min
|
@@ -44,7 +47,7 @@ module Mgmg
|
|
44
47
|
@smith_max = other.smith_max
|
45
48
|
@armor_max = other.armor_max
|
46
49
|
@comp_max = other.comp_max
|
47
|
-
@
|
50
|
+
@comp_ext = other.comp_ext
|
48
51
|
@magdef_maximize = other.magdef_maximize
|
49
52
|
@target_weight = other.target_weight
|
50
53
|
@reinforcement = other.reinforcement.dup
|
@@ -76,7 +79,7 @@ module Mgmg
|
|
76
79
|
self
|
77
80
|
end
|
78
81
|
def set_default(recipe, force: false)
|
79
|
-
if @include_system_equips
|
82
|
+
if (!@system_equips_checked) and @include_system_equips then
|
80
83
|
case recipe
|
81
84
|
when String
|
82
85
|
@include_system_equips = false unless Mgmg::SystemEquipRegexp.values.any?{|re| re.match(recipe)}
|
@@ -85,6 +88,7 @@ module Mgmg
|
|
85
88
|
else
|
86
89
|
raise ArgumentError, 'recipe should be String or Enumerable'
|
87
90
|
end
|
91
|
+
@system_equips_checked = true
|
88
92
|
end
|
89
93
|
update_sa_min(recipe, force)
|
90
94
|
@comp_min = recipe.min_comp(opt: self) if force || @comp_min.nil?
|
data/lib/mgmg/search.rb
CHANGED
@@ -46,6 +46,28 @@ class String
|
|
46
46
|
end
|
47
47
|
opt.comp_max
|
48
48
|
end
|
49
|
+
private def eval_comp(para, target, comp, opt, eo)
|
50
|
+
return [nil, Float::INFINITY] if (comp < opt.comp_min or opt.comp_max < comp)
|
51
|
+
comp -= 1 if ( opt.comp_min<comp and eo & (2**(comp&1)) == 0 )
|
52
|
+
smith = smith_search(para, target, comp, opt:)
|
53
|
+
exp = Mgmg.exp(smith, comp)
|
54
|
+
[[smith, comp], exp]
|
55
|
+
rescue Mgmg::SearchCutException
|
56
|
+
[nil, Float::INFINITY]
|
57
|
+
end
|
58
|
+
private def fine(exp_best, ret, para, target, comp, opt, eo)
|
59
|
+
return [exp_best, ret] if eo & (2**(comp&1)) == 0
|
60
|
+
smith = smith_search(para, target, comp, opt:)
|
61
|
+
exp = Mgmg.exp(smith, comp)
|
62
|
+
if exp < exp_best
|
63
|
+
exp_best, ret = exp, [smith, comp]
|
64
|
+
elsif exp == exp_best
|
65
|
+
if ret.nil? or opt.irep.para_call(para, *ret) < opt.irep.para_call(para, smith, comp) then
|
66
|
+
ret = [smith, comp]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
[exp_best, ret]
|
70
|
+
end
|
49
71
|
def search(para, target, opt: Mgmg::Option.new)
|
50
72
|
opt = opt.dup.set_default(self)
|
51
73
|
opt_nocut = opt.dup; opt_nocut.cut_exp = Float::INFINITY
|
@@ -59,18 +81,39 @@ class String
|
|
59
81
|
opt.cut_exp, ret = exp, [opt.smith_min, opt.comp_max] if exp <= opt.cut_exp
|
60
82
|
exp = Mgmg.exp(opt.smith_max, opt.comp_min)
|
61
83
|
opt.cut_exp, ret = exp, [opt.smith_max, opt.comp_min] if ( exp < opt.cut_exp || (ret.nil? && exp==opt.cut_exp) )
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
if
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
84
|
+
eo = opt.irep.eo_para(para)
|
85
|
+
comps = Mgmg.fib_init(opt.comp_min, opt.comp_max)
|
86
|
+
values = comps.map do |comp|
|
87
|
+
r, e = eval_comp(para, target, comp, opt_nocut, eo)
|
88
|
+
opt.cut_exp, ret = e, r if e < opt.cut_exp
|
89
|
+
e
|
90
|
+
end
|
91
|
+
while 3 < comps[3]-comps[0]
|
92
|
+
if values[1] <= values[2]
|
93
|
+
comp = comps[0] + comps[2]-comps[1]
|
94
|
+
comps = [comps[0], comp, comps[1], comps[2]]
|
95
|
+
r, e = eval_comp(para, target, comp, opt_nocut, eo)
|
96
|
+
opt.cut_exp, ret = e, r if e < opt.cut_exp
|
97
|
+
values = [values[0], e, values[1], values[2]]
|
98
|
+
else
|
99
|
+
comp = comps[1] + comps[3]-comps[2]
|
100
|
+
comps = [comps[1], comps[2], comp, comps[3]]
|
101
|
+
r, e = eval_comp(para, target, comp, opt_nocut, eo)
|
102
|
+
opt.cut_exp, ret = e, r if e < opt.cut_exp
|
103
|
+
values = [values[1], values[2], e, values[3]]
|
72
104
|
end
|
105
|
+
end
|
106
|
+
exp_best = opt.cut_exp
|
107
|
+
opt.cut_exp = exp_best + (exp_best*opt.comp_ext[0]).to_i.clamp(opt.comp_ext[1], opt.comp_ext[2])
|
108
|
+
(comps[0]-1).downto(opt.comp_min) do |comp|
|
109
|
+
exp_best, ret = fine(exp_best, ret, para, target, comp, opt, eo)
|
110
|
+
rescue Mgmg::SearchCutException
|
111
|
+
break
|
112
|
+
end
|
113
|
+
(comps[3]+1).upto(opt.comp_max) do |comp|
|
114
|
+
exp_best, ret = fine(exp_best, ret, para, target, comp, opt, eo)
|
73
115
|
rescue Mgmg::SearchCutException
|
116
|
+
break
|
74
117
|
end
|
75
118
|
if ret.nil?
|
76
119
|
max = opt.irep.para_call(para, *find_max(para, opt.cut_exp, opt:))
|
@@ -90,17 +133,55 @@ class String
|
|
90
133
|
end
|
91
134
|
smith
|
92
135
|
end
|
136
|
+
private def eval_comp_fm(para, comp, eo, opt, max, max_exp)
|
137
|
+
return [-Float::INFINITY, Float::INFINITY] if (comp < opt.comp_min or opt.comp_max < comp)
|
138
|
+
comp -= 1 if ( opt.comp_min<comp and ( eo & (2**(comp&1)) == 0 ) )
|
139
|
+
smith = Mgmg.invexp2(max_exp, comp)
|
140
|
+
cur = opt.irep.para_call(para, smith, comp)
|
141
|
+
smith = minimize_smith(para, smith, comp, cur, opt) if max <= cur
|
142
|
+
[cur, smith]
|
143
|
+
end
|
93
144
|
def find_max(para, max_exp, opt: Mgmg::Option.new)
|
94
145
|
opt = opt.dup.set_default(self)
|
95
146
|
exp = Mgmg.exp(opt.smith_min, opt.comp_min)
|
96
147
|
raise Mgmg::SearchCutException, "the recipe requires #{exp.comma3} experiment points, which exceeds given max_exp=#{max_exp.comma3}" if max_exp < exp
|
97
148
|
ret = [Mgmg.invexp2(max_exp, opt.comp_min), opt.comp_min]
|
98
149
|
max = opt.irep.para_call(para, *ret)
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
150
|
+
eo = opt.irep.eo_para(para)
|
151
|
+
opt.comp_max = Mgmg.invexp2c(max_exp, opt.smith_min)
|
152
|
+
comps = Mgmg.fib_init(opt.comp_min+1, opt.comp_max)
|
153
|
+
values = comps.map do |comp|
|
154
|
+
cur, smith = eval_comp_fm(para, comp, eo, opt, max, max_exp)
|
103
155
|
ret, max = [smith, comp], cur if ( max < cur || ( max == cur && Mgmg.exp(smith, comp) < Mgmg.exp(*ret) ) )
|
156
|
+
cur
|
157
|
+
end
|
158
|
+
while 3 < comps[3]-comps[0]
|
159
|
+
if values[2] <= values[1]
|
160
|
+
comp = comps[0] + comps[2]-comps[1]
|
161
|
+
comps = [comps[0], comp, comps[1], comps[2]]
|
162
|
+
cur, smith = eval_comp_fm(para, comp, eo, opt, max, max_exp)
|
163
|
+
ret, max = [smith, comp], cur if ( max < cur || ( max == cur && Mgmg.exp(smith, comp) < Mgmg.exp(*ret) ) )
|
164
|
+
values = [values[0], cur, values[1], values[2]]
|
165
|
+
else
|
166
|
+
comp = comps[1] + comps[3]-comps[2]
|
167
|
+
comps = [comps[1], comps[2], comp, comps[3]]
|
168
|
+
cur, smith = eval_comp_fm(para, comp, eo, opt, max, max_exp)
|
169
|
+
ret, max = [smith, comp], cur if ( max < cur || ( max == cur && Mgmg.exp(smith, comp) < Mgmg.exp(*ret) ) )
|
170
|
+
values = [values[1], values[2], cur, values[3]]
|
171
|
+
end
|
172
|
+
end
|
173
|
+
th = max - (max*opt.comp_ext[3]).ceil
|
174
|
+
(comps[0]-1).downto(opt.comp_min) do |comp|
|
175
|
+
next if ( eo & (2**(comp&1)) == 0 )
|
176
|
+
cur, smith = eval_comp_fm(para, comp, eo, opt, max, max_exp)
|
177
|
+
ret, max = [smith, comp], cur if ( max < cur || ( max == cur && Mgmg.exp(smith, comp) < Mgmg.exp(*ret) ) )
|
178
|
+
break if cur < th
|
179
|
+
end
|
180
|
+
(comps[3]+1).upto(opt.comp_max) do |comp|
|
181
|
+
next if ( eo & (2**(comp&1)) == 0 )
|
182
|
+
cur, smith = eval_comp_fm(para, comp, eo, opt, max, max_exp)
|
183
|
+
ret, max = [smith, comp], cur if ( max < cur || ( max == cur && Mgmg.exp(smith, comp) < Mgmg.exp(*ret) ) )
|
184
|
+
break if cur < th
|
104
185
|
end
|
105
186
|
ret
|
106
187
|
end
|
@@ -223,6 +304,28 @@ module Enumerable
|
|
223
304
|
end
|
224
305
|
opt.comp_max
|
225
306
|
end
|
307
|
+
private def eval_comp_a(para, target, comp, opt, eo)
|
308
|
+
return [nil, Float::INFINITY] if (comp < opt.comp_min or opt.comp_max < comp)
|
309
|
+
comp -= 1 if ( opt.comp_min<comp and eo & (2**(comp&1)) == 0 )
|
310
|
+
armor = armor_search(para, target, -1, comp, opt:)
|
311
|
+
exp = Mgmg.exp(armor, comp)
|
312
|
+
[[-1, armor, comp], exp]
|
313
|
+
rescue Mgmg::SearchCutException
|
314
|
+
[nil, Float::INFINITY]
|
315
|
+
end
|
316
|
+
private def fine_a(exp_best, ret, para, target, comp, opt, eo)
|
317
|
+
return [exp_best, ret] if eo & (2**(comp&1)) == 0
|
318
|
+
armor = armor_search(para, target, -1, comp, opt:)
|
319
|
+
exp = Mgmg.exp(armor, comp)
|
320
|
+
if exp < exp_best
|
321
|
+
exp_best, ret = exp, [-1, armor, comp]
|
322
|
+
elsif exp == exp_best
|
323
|
+
if ret.nil? or opt.irep.para_call(para, *ret) < opt.irep.para_call(para, -1, armor, comp) then
|
324
|
+
ret = [-1, armor, comp]
|
325
|
+
end
|
326
|
+
end
|
327
|
+
[exp_best, ret]
|
328
|
+
end
|
226
329
|
private def search_aonly(para, target, opt: Mgmg::Option.new)
|
227
330
|
opt_nocut = opt.dup; opt_nocut.cut_exp = Float::INFINITY
|
228
331
|
opt.armor_max = armor_search(para, target, -1, opt.comp_min, opt: opt_nocut)
|
@@ -234,18 +337,39 @@ module Enumerable
|
|
234
337
|
opt.cut_exp, ret = exp, [-1, opt.armor_min, opt.comp_max] if exp <= opt.cut_exp
|
235
338
|
exp = Mgmg.exp(opt.armor_max, opt.comp_min)
|
236
339
|
opt.cut_exp, ret = exp, [-1, opt.armor_max, opt.comp_min] if ( exp < opt.cut_exp || (ret.nil? && exp==opt.cut_exp) )
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
if
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
340
|
+
eo = opt.irep.eo_para(para)
|
341
|
+
comps = Mgmg.fib_init(opt.comp_min, opt.comp_max)
|
342
|
+
values = comps.map do |comp|
|
343
|
+
r, e = eval_comp_a(para, target, comp, opt_nocut, eo)
|
344
|
+
opt.cut_exp, ret = e, r if e < opt.cut_exp
|
345
|
+
e
|
346
|
+
end
|
347
|
+
while 3 < comps[3]-comps[0]
|
348
|
+
if values[1] <= values[2]
|
349
|
+
comp = comps[0] + comps[2]-comps[1]
|
350
|
+
comps = [comps[0], comp, comps[1], comps[2]]
|
351
|
+
r, e = eval_comp_a(para, target, comp, opt_nocut, eo)
|
352
|
+
opt.cut_exp, ret = e, r if e < opt.cut_exp
|
353
|
+
values = [values[0], e, values[1], values[2]]
|
354
|
+
else
|
355
|
+
comp = comps[1] + comps[3]-comps[2]
|
356
|
+
comps = [comps[1], comps[2], comp, comps[3]]
|
357
|
+
r, e = eval_comp_a(para, target, comp, opt_nocut, eo)
|
358
|
+
opt.cut_exp, ret = e, r if e < opt.cut_exp
|
359
|
+
values = [values[1], values[2], e, values[3]]
|
247
360
|
end
|
361
|
+
end
|
362
|
+
exp_best = opt.cut_exp
|
363
|
+
opt.cut_exp = exp_best + (exp_best*opt.comp_ext[0]).to_i.clamp(opt.comp_ext[1], opt.comp_ext[2])
|
364
|
+
(comps[0]-1).downto(opt.comp_min) do |comp|
|
365
|
+
exp_best, ret = fine_a(exp_best, ret, para, target, comp, opt, eo)
|
248
366
|
rescue Mgmg::SearchCutException
|
367
|
+
break
|
368
|
+
end
|
369
|
+
(comps[3]+1).upto(opt.comp_max) do |comp|
|
370
|
+
exp_best, ret = fine_a(exp_best, ret, para, target, comp, opt, eo)
|
371
|
+
rescue Mgmg::SearchCutException
|
372
|
+
break
|
249
373
|
end
|
250
374
|
if ret.nil?
|
251
375
|
max = opt.irep.para_call(para, *find_max(para, opt.cut_exp, opt:))
|
@@ -253,6 +377,28 @@ module Enumerable
|
|
253
377
|
end
|
254
378
|
ret
|
255
379
|
end
|
380
|
+
private def eval_comp_s(para, target, comp, opt, eo)
|
381
|
+
return [nil, Float::INFINITY] if (comp < opt.comp_min or opt.comp_max < comp)
|
382
|
+
comp -= 1 if ( opt.comp_min<comp and eo & (2**(comp&1)) == 0 )
|
383
|
+
smith = smith_search(para, target, -1, comp, opt:)
|
384
|
+
exp = Mgmg.exp(smith, comp)
|
385
|
+
[[smith, -1, comp], exp]
|
386
|
+
rescue Mgmg::SearchCutException
|
387
|
+
[nil, Float::INFINITY]
|
388
|
+
end
|
389
|
+
private def fine_s(exp_best, ret, para, target, comp, opt, eo)
|
390
|
+
return [exp_best, ret] if eo & (2**(comp&1)) == 0
|
391
|
+
smith = smith_search(para, target, -1, comp, opt:)
|
392
|
+
exp = Mgmg.exp(smith, comp)
|
393
|
+
if exp < exp_best
|
394
|
+
exp_best, ret = exp, [smith, -1, comp]
|
395
|
+
elsif exp == exp_best
|
396
|
+
if ret.nil? or opt.irep.para_call(para, *ret) < opt.irep.para_call(para, smith, -1, comp) then
|
397
|
+
ret = [smith, -1, comp]
|
398
|
+
end
|
399
|
+
end
|
400
|
+
[exp_best, ret]
|
401
|
+
end
|
256
402
|
private def search_sonly(para, target, opt: Mgmg::Option.new)
|
257
403
|
opt_nocut = opt.dup; opt_nocut.cut_exp = Float::INFINITY
|
258
404
|
opt.smith_max = smith_search(para, target, -1, opt.comp_min, opt: opt_nocut)
|
@@ -264,18 +410,39 @@ module Enumerable
|
|
264
410
|
opt.cut_exp, ret = exp, [opt.smith_min, -1, opt.comp_max] if exp <= opt.cut_exp
|
265
411
|
exp = Mgmg.exp(opt.smith_max, opt.comp_min)
|
266
412
|
opt.cut_exp, ret = exp, [opt.smith_max, -1, opt.comp_min] if ( exp < opt.cut_exp || (ret.nil? && exp==opt.cut_exp) )
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
if
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
413
|
+
eo = opt.irep.eo_para(para)
|
414
|
+
comps = Mgmg.fib_init(opt.comp_min, opt.comp_max)
|
415
|
+
values = comps.map do |comp|
|
416
|
+
r, e = eval_comp_s(para, target, comp, opt_nocut, eo)
|
417
|
+
opt.cut_exp, ret = e, r if e < opt.cut_exp
|
418
|
+
e
|
419
|
+
end
|
420
|
+
while 3 < comps[3]-comps[0]
|
421
|
+
if values[1] <= values[2]
|
422
|
+
comp = comps[0] + comps[2]-comps[1]
|
423
|
+
comps = [comps[0], comp, comps[1], comps[2]]
|
424
|
+
r, e = eval_comp_s(para, target, comp, opt_nocut, eo)
|
425
|
+
opt.cut_exp, ret = e, r if e < opt.cut_exp
|
426
|
+
values = [values[0], e, values[1], values[2]]
|
427
|
+
else
|
428
|
+
comp = comps[1] + comps[3]-comps[2]
|
429
|
+
comps = [comps[1], comps[2], comp, comps[3]]
|
430
|
+
r, e = eval_comp_s(para, target, comp, opt_nocut, eo)
|
431
|
+
opt.cut_exp, ret = e, r if e < opt.cut_exp
|
432
|
+
values = [values[1], values[2], e, values[3]]
|
277
433
|
end
|
434
|
+
end
|
435
|
+
exp_best = opt.cut_exp
|
436
|
+
opt.cut_exp = exp_best + (exp_best*opt.comp_ext[0]).to_i.clamp(opt.comp_ext[1], opt.comp_ext[2])
|
437
|
+
(comps[0]-1).downto(opt.comp_min) do |comp|
|
438
|
+
exp_best, ret = fine_s(exp_best, ret, para, target, comp, opt, eo)
|
439
|
+
rescue Mgmg::SearchCutException
|
440
|
+
break
|
441
|
+
end
|
442
|
+
(comps[3]+1).upto(opt.comp_max) do |comp|
|
443
|
+
exp_best, ret = fine_s(exp_best, ret, para, target, comp, opt, eo)
|
278
444
|
rescue Mgmg::SearchCutException
|
445
|
+
break
|
279
446
|
end
|
280
447
|
if ret.nil?
|
281
448
|
max = opt.irep.para_call(para, *find_max(para, opt.cut_exp, opt:))
|
@@ -283,6 +450,28 @@ module Enumerable
|
|
283
450
|
end
|
284
451
|
ret
|
285
452
|
end
|
453
|
+
private def eval_comp_sa(para, target, comp, opt, eo)
|
454
|
+
return [nil, Float::INFINITY] if (comp < opt.comp_min or opt.comp_max < comp)
|
455
|
+
comp -= 1 if ( opt.comp_min<comp and eo & (2**(comp&1)) == 0 )
|
456
|
+
sa = sa_search(para, target, comp, opt:)
|
457
|
+
exp = Mgmg.exp(*sa, comp)
|
458
|
+
[[*sa, comp], exp]
|
459
|
+
rescue Mgmg::SearchCutException
|
460
|
+
[nil, Float::INFINITY]
|
461
|
+
end
|
462
|
+
private def fine_sa(exp_best, ret, para, target, comp, opt, eo)
|
463
|
+
return [exp_best, ret] if eo & (2**(comp&1)) == 0
|
464
|
+
sa = sa_search(para, target, comp, opt:)
|
465
|
+
exp = Mgmg.exp(*sa, comp)
|
466
|
+
if exp < exp_best
|
467
|
+
exp_best, ret = exp, [*sa, comp]
|
468
|
+
elsif exp == exp_best
|
469
|
+
if ret.nil? or opt.irep.para_call(para, *ret) < opt.irep.para_call(para, *sa, comp) then
|
470
|
+
ret = [*sa, comp]
|
471
|
+
end
|
472
|
+
end
|
473
|
+
[exp_best, ret]
|
474
|
+
end
|
286
475
|
def search(para, target, opt: Mgmg::Option.new)
|
287
476
|
opt = opt.dup.set_default(self)
|
288
477
|
opt.comp_min = comp_search(para, target, opt.smith_max, opt.armor_max, opt:)
|
@@ -295,18 +484,39 @@ module Enumerable
|
|
295
484
|
opt.comp_max = comp_search(para, target, opt.smith_min, opt.armor_min, opt:)
|
296
485
|
exp = Mgmg.exp(opt.smith_min, opt.armor_min, opt.comp_max)
|
297
486
|
opt.cut_exp, ret = exp, [opt.smith_min, opt.armor_min,opt. comp_max] if exp <= opt.cut_exp
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
if
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
487
|
+
eo = opt.irep.eo_para(para)
|
488
|
+
comps = Mgmg.fib_init(opt.comp_min, opt.comp_max)
|
489
|
+
values = comps.map do |comp|
|
490
|
+
r, e = eval_comp_sa(para, target, comp, opt_nocut, eo)
|
491
|
+
opt.cut_exp, ret = e, r if e < opt.cut_exp
|
492
|
+
e
|
493
|
+
end
|
494
|
+
while 3 < comps[3]-comps[0]
|
495
|
+
if values[1] <= values[2]
|
496
|
+
comp = comps[0] + comps[2]-comps[1]
|
497
|
+
comps = [comps[0], comp, comps[1], comps[2]]
|
498
|
+
r, e = eval_comp_sa(para, target, comp, opt_nocut, eo)
|
499
|
+
opt.cut_exp, ret = e, r if e < opt.cut_exp
|
500
|
+
values = [values[0], e, values[1], values[2]]
|
501
|
+
else
|
502
|
+
comp = comps[1] + comps[3]-comps[2]
|
503
|
+
comps = [comps[1], comps[2], comp, comps[3]]
|
504
|
+
r, e = eval_comp_sa(para, target, comp, opt_nocut, eo)
|
505
|
+
opt.cut_exp, ret = e, r if e < opt.cut_exp
|
506
|
+
values = [values[1], values[2], e, values[3]]
|
308
507
|
end
|
508
|
+
end
|
509
|
+
exp_best = opt.cut_exp
|
510
|
+
opt.cut_exp = exp_best + (exp_best*opt.comp_ext[0]).to_i.clamp(opt.comp_ext[1], opt.comp_ext[2])
|
511
|
+
(comps[0]-1).downto(opt.comp_min) do |comp|
|
512
|
+
exp_best, ret = fine_sa(exp_best, ret, para, target, comp, opt, eo)
|
309
513
|
rescue Mgmg::SearchCutException
|
514
|
+
break
|
515
|
+
end
|
516
|
+
(comps[3]+1).upto(opt.comp_max) do |comp|
|
517
|
+
exp_best, ret = fine_sa(exp_best, ret, para, target, comp, opt, eo)
|
518
|
+
rescue Mgmg::SearchCutException
|
519
|
+
break
|
310
520
|
end
|
311
521
|
if ret.nil?
|
312
522
|
max = opt.irep.para_call(para, *find_max(para, opt.cut_exp, opt:))
|
@@ -327,25 +537,103 @@ module Enumerable
|
|
327
537
|
end
|
328
538
|
smith
|
329
539
|
end
|
540
|
+
private def eval_arm(para, armor, comp, eo, opt, ret, max, max_exp)
|
541
|
+
smith = Mgmg.invexp3(max_exp, armor, comp)
|
542
|
+
cur = opt.irep.para_call(para, smith, armor, comp)
|
543
|
+
smith = minimize_smith(para, smith, armor, comp, cur, opt) if max <= cur
|
544
|
+
ret, max = [smith, armor, comp], cur if ( max < cur || ( max == cur && Mgmg.exp(smith, armor, comp) < Mgmg.exp(*ret) ) )
|
545
|
+
[cur, ret, max]
|
546
|
+
end
|
547
|
+
private def eval_comp_fm(para, comp, eo, opt, ret, max, max_exp)
|
548
|
+
return [-Float::INFINITY, Float::INFINITY, max] if (comp < opt.comp_min or opt.comp_max < comp)
|
549
|
+
comp -= 1 if ( opt.comp_min<comp and ( eo & (2**(comp&1)) == 0 ) )
|
550
|
+
cur = -Float::INFINITY
|
551
|
+
a_max = [opt.armor_max, Mgmg.invexp3(max_exp, opt.smith_min, comp)].min
|
552
|
+
arms = Mgmg.fib_init(opt.armor_min, a_max)
|
553
|
+
a_vals = arms.map do |armor|
|
554
|
+
cur_i, ret, max = eval_arm(para, armor, comp, eo, opt, ret, max, max_exp)
|
555
|
+
cur_i
|
556
|
+
end
|
557
|
+
while 3 < arms[3]-arms[0]
|
558
|
+
if a_vals[2] <= a_vals[1]
|
559
|
+
armor = arms[0] + arms[2]-arms[1]
|
560
|
+
arms = [arms[0], armor, arms[1], arms[2]]
|
561
|
+
cur_i, ret, max = eval_arm(para, armor, comp, eo, opt, ret, max, max_exp)
|
562
|
+
a_vals = [a_vals[0], cur_i, a_vals[1], a_vals[2]]
|
563
|
+
cur = cur_i if cur < cur_i
|
564
|
+
else
|
565
|
+
armor = arms[1] + arms[3]-arms[2]
|
566
|
+
arms = [arms[1], arms[2], armor, arms[3]]
|
567
|
+
cur_i, ret, max = eval_arm(para, armor, comp, eo, opt, ret, max, max_exp)
|
568
|
+
a_vals = [a_vals[1], a_vals[2], cur_i, a_vals[3]]
|
569
|
+
cur = cur_i if cur < cur_i
|
570
|
+
end
|
571
|
+
end
|
572
|
+
th = max - (max*opt.comp_ext[3]).ceil
|
573
|
+
(arms[0]-1).downto(opt.armor_min) do |armor|
|
574
|
+
cur_i, ret, max = eval_arm(para, armor, comp, eo, opt, ret, max, max_exp)
|
575
|
+
break if cur_i < th
|
576
|
+
cur = cur_i if cur < cur_i
|
577
|
+
end
|
578
|
+
(arms[3]+1).upto(a_max) do |armor|
|
579
|
+
cur_i, ret, max = eval_arm(para, armor, comp, eo, opt, ret, max, max_exp)
|
580
|
+
break if cur_i < th
|
581
|
+
cur = cur_i if cur < cur_i
|
582
|
+
end
|
583
|
+
[cur, ret, max]
|
584
|
+
end
|
330
585
|
def find_max(para, max_exp, opt: Mgmg::Option.new)
|
331
586
|
opt = opt.dup.set_default(self)
|
332
587
|
exp = Mgmg.exp(opt.smith_min, opt.armor_min, opt.comp_min)
|
333
588
|
raise Mgmg::SearchCutException, "the recipe requires #{exp.comma3} experiment points, which exceeds given max_exp=#{max_exp.comma3}" if max_exp < exp
|
334
589
|
ret = [Mgmg.invexp3(max_exp, opt.armor_min, opt.comp_min), opt.armor_min, opt.comp_min]
|
335
590
|
max = opt.irep.para_call(para, *ret)
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
591
|
+
eo = opt.irep.eo_para(para)
|
592
|
+
opt.comp_max = [opt.comp_max, Mgmg.invexp3c(max_exp, opt.smith_min, opt.armor_min)].min
|
593
|
+
comps = Mgmg.fib_init(opt.comp_min, opt.comp_max)
|
594
|
+
values = comps.map do |comp|
|
595
|
+
cur, ret, max = eval_comp_fm(para, comp, eo, opt, ret, max, max_exp)
|
596
|
+
cur
|
597
|
+
end
|
598
|
+
while 3 < comps[3]-comps[0]
|
599
|
+
if values[2] <= values[1]
|
600
|
+
comp = comps[0] + comps[2]-comps[1]
|
601
|
+
comps = [comps[0], comp, comps[1], comps[2]]
|
602
|
+
cur, ret, max = eval_comp_fm(para, comp, eo, opt, ret, max, max_exp)
|
603
|
+
values = [values[0], cur, values[1], values[2]]
|
604
|
+
else
|
605
|
+
comp = comps[1] + comps[3]-comps[2]
|
606
|
+
comps = [comps[1], comps[2], comp, comps[3]]
|
607
|
+
cur, ret, max = eval_comp_fm(para, comp, eo, opt, ret, max, max_exp)
|
608
|
+
values = [values[1], values[2], cur, values[3]]
|
343
609
|
end
|
344
610
|
end
|
611
|
+
th = max - (max*opt.comp_ext[3]).ceil
|
612
|
+
(comps[0]-1).downto(opt.comp_min) do |comp|
|
613
|
+
next if ( eo & (2**(comp&1)) == 0 )
|
614
|
+
cur, ret, max = eval_comp_fm(para, comp, eo, opt, ret, max, max_exp)
|
615
|
+
break if cur < th
|
616
|
+
end
|
617
|
+
(comps[3]+1).upto(opt.comp_max) do |comp|
|
618
|
+
next if ( eo & (2**(comp&1)) == 0 )
|
619
|
+
cur, ret, max = eval_comp_fm(para, comp, eo, opt, ret, max, max_exp)
|
620
|
+
break if cur < th
|
621
|
+
end
|
345
622
|
ret
|
346
623
|
end
|
347
624
|
end
|
348
625
|
|
626
|
+
class << Mgmg
|
627
|
+
def fib_init(min, max)
|
628
|
+
z = min-1
|
629
|
+
a, b = 2, 3
|
630
|
+
while z + b < max do
|
631
|
+
a, b = b, a+b
|
632
|
+
end
|
633
|
+
[z, z+b-a, z+a, z+b]
|
634
|
+
end
|
635
|
+
end
|
636
|
+
|
349
637
|
module Mgmg
|
350
638
|
Eighth = 1.quo(8)
|
351
639
|
module_function def find_lowerbound(a, b, para, start, term, opt_a: Option.new, opt_b: Option.new)
|
@@ -430,7 +718,6 @@ module Mgmg
|
|
430
718
|
raise Mgmg::SearchCutException, "given recipes are equivalent at start target=#{start.comma3}"
|
431
719
|
end
|
432
720
|
|
433
|
-
|
434
721
|
loop do
|
435
722
|
loop do
|
436
723
|
foo = a.find_max(para, eb, opt: opt_a)
|
@@ -444,38 +731,40 @@ module Mgmg
|
|
444
731
|
eb = foo
|
445
732
|
end
|
446
733
|
ea = Mgmg.exp(*sca)
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
res += 1
|
734
|
+
pb = opt_b.irep.para_call(para, *scb)
|
735
|
+
if ea <= eb and pb <= pa and (ea+pb)!=(eb+pa) then
|
736
|
+
until pa < pb
|
737
|
+
scb = b.search(para, pb+Eighth, opt: opt_b)
|
738
|
+
pb = opt_b.irep.para_call(para, *scb)
|
453
739
|
end
|
740
|
+
return [pa, pb]
|
741
|
+
elsif ea < eb
|
742
|
+
return [pa, pb] if scb == b.search(para, pa, opt: opt_b)
|
743
|
+
end
|
744
|
+
tag = [ea, eb].min - 1
|
745
|
+
begin
|
746
|
+
scb = b.find_max(para, tag, opt: opt_b)
|
747
|
+
rescue Mgmg::SearchCutException
|
748
|
+
eb, pb = Mgmg.exp(*scb), opt_b.irep.para_call(para, *scb)
|
454
749
|
begin
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
pa, pb = opt_a.irep.para_call(para, *sca), opt_b.irep.para_call(para, *scb)
|
461
|
-
if ea < eb || ( ea == eb && pb < pa )
|
462
|
-
until pa < pb
|
463
|
-
scb = b.search(para, pb+Eighth, opt: opt_b)
|
464
|
-
pb = opt_b.irep.para_call(para, *scb)
|
750
|
+
sca = a.find_max(para, eb, opt: opt_a)
|
751
|
+
ea, pa = Mgmg.exp(*sca), opt_a.irep.para_call(para, *sca)
|
752
|
+
while eb <= ea
|
753
|
+
sca = a.find_max(para, ea-1, opt: opt_a)
|
754
|
+
ea, pa = Mgmg.exp(*sca), opt_a.irep.para_call(para, *sca)
|
465
755
|
end
|
466
|
-
|
467
|
-
elsif res == 2
|
756
|
+
rescue Mgmg::SearchCutException
|
468
757
|
raise Mgmg::SearchCutException, "given recipes are never reversed from the start target=#{start.comma3} until #{pa.comma3}"
|
469
758
|
end
|
470
|
-
end
|
471
|
-
if ea < eb
|
472
|
-
pb = opt_b.irep.para_call(para, *scb)
|
473
|
-
until pa < pb
|
474
|
-
scb = b.search(para, pb+Eighth, opt: opt_b)
|
475
|
-
pb = opt_b.irep.para_call(para, *scb)
|
476
|
-
end
|
477
759
|
return [pa, pb]
|
478
760
|
end
|
761
|
+
begin
|
762
|
+
sca = a.find_max(para, tag, opt: opt_a)
|
763
|
+
rescue Mgmg::SearchCutException
|
764
|
+
raise Mgmg::SearchCutException, "given recipes are never reversed from the start target=#{start.comma3} until #{opt_a.irep.para_call(para, *sca).comma3}"
|
765
|
+
end
|
766
|
+
ea, eb = Mgmg.exp(*sca), Mgmg.exp(*scb)
|
767
|
+
pa, pb = opt_a.irep.para_call(para, *sca), opt_b.irep.para_call(para, *scb)
|
479
768
|
end
|
480
769
|
raise UnexpectedError
|
481
770
|
end
|
data/lib/mgmg/version.rb
CHANGED
data/reference.md
CHANGED
@@ -130,12 +130,9 @@
|
|
130
130
|
`opt`は,`comp_min`,`comp_max`,`left_associative`,`reinforcement`,`irep`を使用します.
|
131
131
|
|
132
132
|
## `String#search(para, target, opt: Mgmg.option())`
|
133
|
-
|
134
|
-
`smith_search`を行い,その過程で得られた最小経験値の鍛冶・防具製作Lvと道具製作Lvからなる配列を返します.
|
135
|
-
レシピ中の,対象パラメータの種別値がすべて奇数,または全て偶数であるなら,`opt.step`を`2`にしても探索すべき範囲を網羅できます.
|
136
|
-
その他は`String#smith_seach`と同様です.
|
133
|
+
合計経験値が最小となる鍛冶・防具製作Lvと道具製作Lvの組を探索します.道具製作Lvを決定変数として,`smith_search`の返り値との合計経験値を最小化する道具製作Lvをフィボナッチ探索で探索した後,得られた解 c の前後を探索し,最適化します.道具製作Lv c における合計経験値`exp`に対して,`exp+(exp*opt.comp_ext[0]).to_i.clamp(opt.comp_ext[1], opt.comp_ext[2])`を超えない範囲を探索します.この目的関数は単峰ではないため,フィボナッチ探索のみでは最適解が得られないことがあります.
|
137
134
|
|
138
|
-
|
135
|
+
その他は`String#smith_seach`と同様で,`opt`は,`String#smith_search`または`String#comp_search`で使われるすべてのパラメータと,`comp_ext`を使用します.
|
139
136
|
|
140
137
|
## `Enumerable#search(para, target, opt: Mgmg.option())`
|
141
138
|
複数装備の組について,`para`の値が`target`以上となる最小経験値の`[鍛冶Lv,防具製作Lv,道具製作Lv]`を返します.
|
@@ -143,11 +140,13 @@
|
|
143
140
|
|
144
141
|
`opt.smith_min`および`opt.armor_min`のデフォルト値は,武器・防具の各総重量を`opt.target_weight`で製作するのに必要な鍛冶・防具製作Lv (`self.min_level(*opt.target_weight)`)です.`opt.target_weight`には2要素からなる配列を指定しますが,整数が与えられた場合,それを2つ並べた配列と同等のものとして扱います.合計重量を指定することにはならないので注意してください.`opt.target_weight`のデフォルト値は`0`です.
|
145
142
|
|
146
|
-
その他は,`String#
|
143
|
+
その他は,`String#seach`と同様です.
|
147
144
|
|
148
145
|
## `String#find_max(para, max_exp, opt: Mgmg.option())`
|
149
146
|
経験値の合計が`max_exp`以下の範囲で,`para`の値が最大となる鍛冶・防具製作Lvと道具製作Lvからなる配列を返します.`para`の値が最大となる組が複数存在する場合,経験値が小さい方が優先されます.
|
150
147
|
|
148
|
+
`String#search`と同様に,道具製作Lvをフィボナッチ探索で探索した後,得られた解 c の前後を探索し,最適化します.道具製作Lv c における最大の`para`値`p_c`に対して,`p_c-(p_c*opt.comp_ext[3]).ceil`を下回らない範囲を探索します.
|
149
|
+
|
151
150
|
## `Enumerable#find_max(para, max_exp, opt: Mgmg.option())`
|
152
151
|
複数装備の組について,経験値の合計が`max_exp`以下の範囲で,`para`の値が最大となる鍛冶Lv,防具製作Lv,道具製作Lvからなる配列を返します.`para`の値が最大となる組が複数存在する場合,経験値が小さい方が優先されます.
|
153
152
|
|
@@ -455,9 +454,9 @@ alias として`*`があるほか`scalar(1.quo(value))`として`quo`,`/`,`s
|
|
455
454
|
|smith_min|`recipe.min_level(target_weight)`|非対応|鍛冶Lvに関する探索範囲の最小値|`String#search`など|
|
456
455
|
|armor_min|`recipe.min_level(*target_weight)[1]`|非対応|防具製作Lvに関する探索範囲の最小値|`Enumerable#search`など.`String`系では代わりに`smith_min`を使う|
|
457
456
|
|comp_min|`recipe.min_comp`|非対応|道具製作Lvに関する探索範囲の最小値|`String#search`など|
|
458
|
-
|smith_max, armor_max, comp_max|`
|
457
|
+
|smith_max, armor_max, comp_max|`1_000_000_000`|対応|各製作Lvの探索範囲の最大値|`String#search`など|
|
458
|
+
|comp_ext|`[0.1, 100, 10_000, 0.1]`|対応|フィボナッチ探索後に追加探索を行う範囲|`String#search`など|
|
459
459
|
|target_weight|`0`|非対応|`smith_min`のデフォルト値計算に使う目標重量|`String#search`など|
|
460
|
-
|step|`1`|非対応|探索時において道具製作Lvを動かす幅|`String#search`など|
|
461
460
|
|magdef_maximize|`true`|非対応|目標を魔防最大(真)かコスト最小(偽)にするためのスイッチ|`String#phydef_optimize`|
|
462
461
|
|reinforcement|`[]`|非対応|[前述](#mgmgequipreinforcearg)の`Mgmg::Equip#reinforce`による強化リスト|一部を除くすべてのメソッド|
|
463
462
|
|buff|`[]`|非対応|`reinforcement`のエイリアス|どちらか一方のみを指定する|
|
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.
|
4
|
+
version: 1.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- KAZOON
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-11-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -104,7 +104,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
104
104
|
- !ruby/object:Gem::Version
|
105
105
|
version: '0'
|
106
106
|
requirements: []
|
107
|
-
rubygems_version: 3.3.
|
107
|
+
rubygems_version: 3.3.25
|
108
108
|
signing_key:
|
109
109
|
specification_version: 4
|
110
110
|
summary: Calculate specs of equipments of Megurimeguru, a game produced by Kou.
|