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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b9da45e92b9c5fb1cae6577e532cce280c997f46c1df019514b1eb976d89f03f
4
- data.tar.gz: 6f905a8d05126dbc81c71e73ddd64988e96a2033f520f6ded8b595ac96eb06be
3
+ metadata.gz: 0c14a55ea3bffa94e0b9aa9e65a7a5013d34d8bec62179baae302dabdd150f08
4
+ data.tar.gz: db35e0738dc57da24e132bacea2cd30a1040d6dc563449ce3b3b8b19a62fa0ae
5
5
  SHA512:
6
- metadata.gz: cfaf9d22a5aaa36fdef9c15348117a014a4a9385947417a614a3f1fc82622ee4f1ad4c0d5615cbde9dded71b4a2a13edb88938e0fc88c818fafe27625b8f9885
7
- data.tar.gz: 46734977a74542248d65449eb9b49aa4c9ce6f5b875a194c1ec8db6bdab47856fa341c9c77ef29f28c76dd4d078899260da64a6c8d04510fd7b4c692af8581c1
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
- def initialize(kind, star, main_m, sub_m, para, rein=[])
140
- @kind, @star, @main, @sub, @para = kind, star, main_m, sub_m, para
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
- ret = if s.nil?
188
- @para[i]
189
- elsif x.nil?
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(main_k, main_s+sub_s, main_sub, sub_main, para)
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
- ret = new(kind, (main_s+sub_s).div(2), main_mc, sub_mc, para)
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
- new(equip.kind, equip.star, equip.main, equip.sub, para)
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: 10_000, armor_max: 10_000, comp_max: 10_000
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
- step: 1, magdef_maximize: true,
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
- @step = step
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 :step, :magdef_maximize, :target_weight, :reinforcement, :irep, :cut_exp, :include_system_equips
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
- @step = other.step
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
- (opt.comp_min+opt.step).step(opt.comp_max-1, opt.step) do |comp|
63
- break if opt.cut_exp < Mgmg.exp(opt.smith_min, comp)
64
- smith = smith_search(para, target, comp, opt:)
65
- exp = Mgmg.exp(smith, comp)
66
- if exp < opt.cut_exp
67
- opt.cut_exp, ret = exp, [smith, comp]
68
- elsif exp == opt.cut_exp
69
- if ret.nil? or opt.irep.para_call(para, *ret) < opt.irep.para_call(para, smith, comp) then
70
- ret = [smith, comp]
71
- end
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
- (opt.comp_min+1).upto(Mgmg.invexp2c(max_exp, opt.smith_min)) do |comp|
100
- smith = Mgmg.invexp2(max_exp, comp)
101
- cur = opt.irep.para_call(para, smith, comp)
102
- smith = minimize_smith(para, smith, comp, cur, opt) if max <= cur
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
- (opt.comp_min+opt.step).step(opt.comp_max-1, opt.step) do |comp|
238
- break if opt.cut_exp < Mgmg.exp(opt.armor_min, comp)
239
- armor = armor_search(para, target, -1, comp, opt:)
240
- exp = Mgmg.exp(armor, comp)
241
- if exp < opt.cut_exp
242
- opt.cut_exp, ret = exp, [-1, armor, comp]
243
- elsif exp == opt.cut_exp
244
- if ret.nil? or opt.irep.para_call(para, *ret) < opt.irep.para_call(para, -1, armor, comp) then
245
- ret = [-1, armor, comp]
246
- end
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
- (opt.comp_min+opt.step).step(opt.comp_max-1, opt.step) do |comp|
268
- break if opt.cut_exp < Mgmg.exp(opt.smith_min, comp)
269
- smith = smith_search(para, target, -1, comp, opt:)
270
- exp = Mgmg.exp(smith, comp)
271
- if exp < opt.cut_exp
272
- opt.cut_exp, ret = exp, [smith, -1, comp]
273
- elsif exp == opt.cut_exp
274
- if ret.nil? or opt.irep.para_call(para, *ret) < opt.irep.para_call(para, smith, -1, comp) then
275
- ret = [smith, -1, comp]
276
- end
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
- (opt.comp_min).upto(opt.comp_max-1) do |comp|
299
- break if opt.cut_exp < Mgmg.exp(opt.smith_min, opt.armor_min, comp)
300
- smith, armor = sa_search(para, target, comp, opt:)
301
- exp = Mgmg.exp(smith, armor, comp)
302
- if exp < opt.cut_exp
303
- opt.cut_exp, ret = exp, [smith, armor, comp]
304
- elsif exp == opt.cut_exp
305
- if ret.nil? or opt.irep.para_call(para, *ret) < opt.irep.para_call(para, smith, armor, comp) then
306
- ret = [smith, armor, comp]
307
- end
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
- (opt.comp_min).step(Mgmg.invexp3c(max_exp, opt.smith_min, opt.armor_min), opt.step) do |comp|
337
- opt.armor_min.upto(Mgmg.invexp3(max_exp, opt.smith_min, comp)) do |armor|
338
- smith = Mgmg.invexp3(max_exp, armor, comp)
339
- cur = opt.irep.para_call(para, smith, armor, comp)
340
- smith = minimize_smith(para, smith, armor, comp, cur, opt) if max <= cur
341
- ret, max = [smith, armor, comp], cur if ( max < cur || ( max == cur && Mgmg.exp(smith, armor, comp) < Mgmg.exp(*ret) ) )
342
- break if armor < 0
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
- while ea==eb do
448
- res = 0
449
- begin
450
- sca = a.find_max(para, ea-1, opt: opt_a)
451
- rescue Mgmg::SearchCutException
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
- scb = b.find_max(para, eb-1, opt: opt_b)
456
- rescue Mgmg::SearchCutException
457
- res += 1
458
- end
459
- ea, eb = Mgmg.exp(*sca), Mgmg.exp(*scb)
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
- return [pa, pb]
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
@@ -1,3 +1,3 @@
1
1
  module Mgmg
2
- VERSION = "1.6.1"
2
+ VERSION = "1.7.0"
3
3
  end
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
- 道具製作Lv `c_min=comp_search(para, target, opt.smith_max, opt: opt)` から `c_max=comp_search(para, target, opt.smith_min, opt: opt)` まで,`opt.step`ずつ動かして,
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
- `opt`は,`String#smith_search`または`String#comp_search`で使われるすべてのパラメータを使用します.
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#smith_seach`と同様です.
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|`10,000`|対応|各製作Lvの探索範囲の最大値|`String#search`など|
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.6.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-10-21 00:00:00.000000000 Z
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.24
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.