mgmg 1.6.2 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0aee0bc2d348dfc0e23ce8556cea710f7645b6a590145f67bc1d5715b8c999a1
4
- data.tar.gz: eed968af79443fabb9b65e931b93079a5dd8c839af6ea0b6f84a62c1ad411eda
3
+ metadata.gz: 79211ca6cd5422500f7dc957eadfb2c1812459c98fd39b84fc9a3ebf178174ec
4
+ data.tar.gz: efa0254194363114aee22993e78cab9359e9a7c92f454d5fffc12b1d14368e6c
5
5
  SHA512:
6
- metadata.gz: 218e8c123428a6ec6228c1d296973c8ec01ba77d09430c3e33e49df081364b2e3e295b4f02a480b5349a709dd26e955e8472852e872ba73dd87b2044ec690904
7
- data.tar.gz: 1c9db46a5f5fe0fdcb76bc0fbeab261414e2d9df4661d3dfe91f137ab066e739584170695fc58868d5d64e1ddec578866a90fd9291c9a47eca31f3cfafab915d
6
+ metadata.gz: 822a754eeeec7b9b9e8d6f1497041c153a56b62d190da8101f798e9b78334cc64406f35167623387c346b9f2453027a7e17a3ebaefc2ec4d38cbfcbe381b01e6
7
+ data.tar.gz: f43028c621ea4025123c1ca338f46c8965d545a8f6a92569cc5bb7f5279d11ea01be0dbe6453c1ffe06c831c55be1cdf439ed66b81fa8e08a4d01ac994348e5f
data/CHANGELOG.md CHANGED
@@ -177,3 +177,15 @@
177
177
 
178
178
  ## 1.6.2 2022/10/31
179
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に変更.
187
+
188
+ ## 1.8.0 2022/11/14
189
+ - オプション`comp_ext`を`fib_ext`に変更し,追加探索の範囲を修正.
190
+ - オプション`smith/armor/comp_max`の扱い方を修正し,デフォルト値を10^5に変更.
191
+ - `Enumerable#find_max`,`Mgmg.#find_lowerbound`のバグを修正.
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: 100_000, armor_max: 100_000, comp_max: 100_000,
6
+ fib_ext: [4, 10]
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
+ fib_ext: Defaults[:fib_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
+ @fib_ext = fib_ext
23
25
  @magdef_maximize = magdef_maximize
24
26
  @target_weight = target_weight
25
27
  @reinforcement = reinforcement
@@ -35,8 +37,8 @@ module Mgmg
35
37
  @include_system_equips = include_system_equips
36
38
  @system_equips_checked = false
37
39
  end
38
- attr_accessor :left_associative, :smith_min, :armor_min, :comp_min, :smith_max, :armor_max, :comp_max
39
- attr_accessor :step, :magdef_maximize, :target_weight, :reinforcement, :irep, :cut_exp, :include_system_equips, :system_equips_checked
40
+ attr_accessor :left_associative, :smith_min, :armor_min, :comp_min, :smith_max, :armor_max, :comp_max, :fib_ext
41
+ attr_accessor :magdef_maximize, :target_weight, :reinforcement, :irep, :cut_exp, :include_system_equips, :system_equips_checked
40
42
  def initialize_copy(other)
41
43
  @left_associative = other.left_associative
42
44
  @smith_min = other.smith_min
@@ -45,7 +47,7 @@ module Mgmg
45
47
  @smith_max = other.smith_max
46
48
  @armor_max = other.armor_max
47
49
  @comp_max = other.comp_max
48
- @step = other.step
50
+ @fib_ext = other.fib_ext.dup
49
51
  @magdef_maximize = other.magdef_maximize
50
52
  @target_weight = other.target_weight
51
53
  @reinforcement = other.reinforcement.dup
data/lib/mgmg/search.rb CHANGED
@@ -34,7 +34,7 @@ class String
34
34
  if target <= opt.irep.para_call(para, smith, opt.comp_min)
35
35
  return opt.comp_min
36
36
  elsif opt.irep.para_call(para, smith, opt.comp_max) < target
37
- raise Mgmg::SearchCutException
37
+ raise Mgmg::SearchCutException, "given comp_max=#{opt.comp_max} does not satisfies the target"
38
38
  end
39
39
  while 1 < opt.comp_max - opt.comp_min do
40
40
  comp = (opt.comp_max - opt.comp_min).div(2) + opt.comp_min
@@ -46,10 +46,37 @@ 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
52
- opt.comp_min = comp_search(para, target, opt.smith_max, opt:)
74
+ begin
75
+ opt.comp_min = comp_search(para, target, opt.smith_max, opt:)
76
+ rescue Mgmg::SearchCutException
77
+ foo = opt.irep.para_call(para, opt.smith_max, opt.comp_max)
78
+ raise Mgmg::SearchCutException, "#{self} could not reach target=#{target} until (smith_max, comp_max)=(#{opt.smith_max}, #{opt.comp_max}), which yields #{foo.comma3}"
79
+ end
53
80
  opt.smith_max = smith_search(para, target, opt.comp_min, opt: opt_nocut)
54
81
  opt.smith_min = smith_search(para, target, opt.comp_max, opt: opt_nocut)
55
82
  raise Mgmg::SearchCutException if opt.cut_exp < Mgmg.exp(opt.smith_min, opt.comp_min)
@@ -59,18 +86,40 @@ class String
59
86
  opt.cut_exp, ret = exp, [opt.smith_min, opt.comp_max] if exp <= opt.cut_exp
60
87
  exp = Mgmg.exp(opt.smith_max, opt.comp_min)
61
88
  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
89
+ eo = opt.irep.eo_para(para)
90
+ comps = Mgmg.fib_init(opt.comp_min, opt.comp_max)
91
+ values = comps.map do |comp|
92
+ r, e = eval_comp(para, target, comp, opt_nocut, eo)
93
+ opt.cut_exp, ret = e, r if e < opt.cut_exp
94
+ e
95
+ end
96
+ while 3 < comps[3]-comps[0]
97
+ if values[1] <= values[2]
98
+ comp = comps[0] + comps[2]-comps[1]
99
+ comps = [comps[0], comp, comps[1], comps[2]]
100
+ r, e = eval_comp(para, target, comp, opt_nocut, eo)
101
+ opt.cut_exp, ret = e, r if e < opt.cut_exp
102
+ values = [values[0], e, values[1], values[2]]
103
+ else
104
+ comp = comps[1] + comps[3]-comps[2]
105
+ comps = [comps[1], comps[2], comp, comps[3]]
106
+ r, e = eval_comp(para, target, comp, opt_nocut, eo)
107
+ opt.cut_exp, ret = e, r if e < opt.cut_exp
108
+ values = [values[1], values[2], e, values[3]]
72
109
  end
110
+ end
111
+ exp_best = opt.cut_exp
112
+ diff = values.max-values.min
113
+ opt.cut_exp = exp_best + diff*opt.fib_ext[0]
114
+ (comps[0]-1).downto(opt.comp_min) do |comp|
115
+ exp_best, ret = fine(exp_best, ret, para, target, comp, opt, eo)
116
+ rescue Mgmg::SearchCutException
117
+ break
118
+ end
119
+ (comps[3]+1).upto(opt.comp_max) do |comp|
120
+ exp_best, ret = fine(exp_best, ret, para, target, comp, opt, eo)
73
121
  rescue Mgmg::SearchCutException
122
+ break
74
123
  end
75
124
  if ret.nil?
76
125
  max = opt.irep.para_call(para, *find_max(para, opt.cut_exp, opt:))
@@ -90,17 +139,56 @@ class String
90
139
  end
91
140
  smith
92
141
  end
142
+ private def eval_comp_fm(para, comp, eo, opt, max, max_exp)
143
+ return [-Float::INFINITY, Float::INFINITY] if (comp < opt.comp_min or opt.comp_max < comp)
144
+ comp -= 1 if ( opt.comp_min<comp and ( eo & (2**(comp&1)) == 0 ) )
145
+ smith = Mgmg.invexp2(max_exp, comp)
146
+ cur = opt.irep.para_call(para, smith, comp)
147
+ smith = minimize_smith(para, smith, comp, cur, opt) if max <= cur
148
+ [cur, smith]
149
+ end
93
150
  def find_max(para, max_exp, opt: Mgmg::Option.new)
94
151
  opt = opt.dup.set_default(self)
95
152
  exp = Mgmg.exp(opt.smith_min, opt.comp_min)
96
153
  raise Mgmg::SearchCutException, "the recipe requires #{exp.comma3} experiment points, which exceeds given max_exp=#{max_exp.comma3}" if max_exp < exp
97
154
  ret = [Mgmg.invexp2(max_exp, opt.comp_min), opt.comp_min]
98
155
  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
156
+ eo = opt.irep.eo_para(para)
157
+ opt.comp_max = Mgmg.invexp2c(max_exp, opt.smith_min)
158
+ comps = Mgmg.fib_init(opt.comp_min+1, opt.comp_max)
159
+ values = comps.map do |comp|
160
+ cur, smith = eval_comp_fm(para, comp, eo, opt, max, max_exp)
161
+ ret, max = [smith, comp], cur if ( max < cur || ( max == cur && Mgmg.exp(smith, comp) < Mgmg.exp(*ret) ) )
162
+ cur
163
+ end
164
+ while 3 < comps[3]-comps[0]
165
+ if values[2] <= values[1]
166
+ comp = comps[0] + comps[2]-comps[1]
167
+ comps = [comps[0], comp, comps[1], comps[2]]
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[0], cur, values[1], values[2]]
171
+ else
172
+ comp = comps[1] + comps[3]-comps[2]
173
+ comps = [comps[1], comps[2], comp, comps[3]]
174
+ cur, smith = eval_comp_fm(para, comp, eo, opt, max, max_exp)
175
+ ret, max = [smith, comp], cur if ( max < cur || ( max == cur && Mgmg.exp(smith, comp) < Mgmg.exp(*ret) ) )
176
+ values = [values[1], values[2], cur, values[3]]
177
+ end
178
+ end
179
+ diff = values.max-values.min
180
+ th = max - diff*opt.fib_ext[1]
181
+ (comps[0]-1).downto(opt.comp_min) do |comp|
182
+ next if ( eo & (2**(comp&1)) == 0 )
183
+ cur, smith = eval_comp_fm(para, comp, eo, opt, max, max_exp)
103
184
  ret, max = [smith, comp], cur if ( max < cur || ( max == cur && Mgmg.exp(smith, comp) < Mgmg.exp(*ret) ) )
185
+ break if cur < th
186
+ end
187
+ (comps[3]+1).upto(opt.comp_max) do |comp|
188
+ next if ( eo & (2**(comp&1)) == 0 )
189
+ cur, smith = eval_comp_fm(para, comp, eo, opt, max, max_exp)
190
+ ret, max = [smith, comp], cur if ( max < cur || ( max == cur && Mgmg.exp(smith, comp) < Mgmg.exp(*ret) ) )
191
+ break if cur < th
104
192
  end
105
193
  ret
106
194
  end
@@ -211,7 +299,7 @@ module Enumerable
211
299
  if target <= opt.irep.para_call(para, smith, armor, opt.comp_min)
212
300
  return opt.comp_min
213
301
  elsif opt.irep.para_call(para, smith, armor, opt.comp_max) < target
214
- raise ArgumentError, "given comp_max=#{opt.comp_max} does not satisfies the target"
302
+ raise Mgmg::SearchCutException, "given comp_max=#{opt.comp_max} does not satisfies the target"
215
303
  end
216
304
  while 1 < opt.comp_max - opt.comp_min do
217
305
  comp = (opt.comp_max - opt.comp_min).div(2) + opt.comp_min
@@ -223,90 +311,79 @@ module Enumerable
223
311
  end
224
312
  opt.comp_max
225
313
  end
226
- private def search_aonly(para, target, opt: Mgmg::Option.new)
227
- opt_nocut = opt.dup; opt_nocut.cut_exp = Float::INFINITY
228
- opt.armor_max = armor_search(para, target, -1, opt.comp_min, opt: opt_nocut)
229
- opt.armor_min = armor_search(para, target, -1, opt.comp_max, opt: opt_nocut)
230
- raise Mgmg::SearchCutException if opt.cut_exp < Mgmg.exp(opt.armor_min, opt.comp_min)
231
- opt.comp_max = comp_search(para, target, -1, opt.armor_min, opt:)
232
- ret = nil
233
- exp = Mgmg.exp(opt.armor_min, opt.comp_max)
234
- opt.cut_exp, ret = exp, [-1, opt.armor_min, opt.comp_max] if exp <= opt.cut_exp
235
- exp = Mgmg.exp(opt.armor_max, opt.comp_min)
236
- 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
247
- end
248
- rescue Mgmg::SearchCutException
249
- end
250
- if ret.nil?
251
- max = opt.irep.para_call(para, *find_max(para, opt.cut_exp, opt:))
252
- raise Mgmg::SearchCutException, "the maximum output with given cut_exp=#{opt.cut_exp.comma3} is #{max.comma3}, which does not reach given target=#{target.comma3}"
253
- end
254
- ret
314
+ private def eval_comp_sa(para, target, comp, opt, eo)
315
+ return [nil, Float::INFINITY] if (comp < opt.comp_min or opt.comp_max < comp)
316
+ comp -= 1 if ( opt.comp_min<comp and eo & (2**(comp&1)) == 0 )
317
+ sa = sa_search(para, target, comp, opt:)
318
+ exp = Mgmg.exp(*sa, comp)
319
+ [[*sa, comp], exp]
320
+ rescue Mgmg::SearchCutException
321
+ [nil, Float::INFINITY]
255
322
  end
256
- private def search_sonly(para, target, opt: Mgmg::Option.new)
257
- opt_nocut = opt.dup; opt_nocut.cut_exp = Float::INFINITY
258
- opt.smith_max = smith_search(para, target, -1, opt.comp_min, opt: opt_nocut)
259
- opt.smith_min = smith_search(para, target, -1, opt.comp_max, opt: opt_nocut)
260
- raise Mgmg::SearchCutException if opt.cut_exp < Mgmg.exp(opt.smith_min, opt.comp_min)
261
- opt.comp_max = comp_search(para, target, opt.smith_min, -1, opt:)
262
- ret = nil
263
- exp = Mgmg.exp(opt.smith_min, opt.comp_max)
264
- opt.cut_exp, ret = exp, [opt.smith_min, -1, opt.comp_max] if exp <= opt.cut_exp
265
- exp = Mgmg.exp(opt.smith_max, opt.comp_min)
266
- 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
323
+ private def fine_sa(exp_best, ret, para, target, comp, opt, eo)
324
+ return [exp_best, ret] if eo & (2**(comp&1)) == 0
325
+ sa = sa_search(para, target, comp, opt:)
326
+ exp = Mgmg.exp(*sa, comp)
327
+ if exp < exp_best
328
+ exp_best, ret = exp, [*sa, comp]
329
+ elsif exp == exp_best
330
+ if ret.nil? or opt.irep.para_call(para, *ret) < opt.irep.para_call(para, *sa, comp) then
331
+ ret = [*sa, comp]
277
332
  end
278
- rescue Mgmg::SearchCutException
279
333
  end
280
- if ret.nil?
281
- max = opt.irep.para_call(para, *find_max(para, opt.cut_exp, opt:))
282
- raise Mgmg::SearchCutException, "the maximum output with given cut_exp=#{opt.cut_exp.comma3} is #{max.comma3}, which does not reach given target=#{target.comma3}"
283
- end
284
- ret
334
+ [exp_best, ret]
285
335
  end
286
336
  def search(para, target, opt: Mgmg::Option.new)
287
337
  opt = opt.dup.set_default(self)
288
- opt.comp_min = comp_search(para, target, opt.smith_max, opt.armor_max, opt:)
338
+ begin
339
+ opt.comp_min = comp_search(para, target, opt.smith_max, opt.armor_max, opt:)
340
+ rescue Mgmg::SearchCutException
341
+ foo = opt.irep.para_call(para, opt.smith_max, opt.armor_max, opt.comp_max)
342
+ raise Mgmg::SearchCutException, "#{self} could not reach target=#{target} until (smith_max, armor_max, comp_max)=(#{opt.smith_max}, #{opt.armor_max}, #{opt.comp_max}), which yields #{foo.comma3}"
343
+ end
289
344
  opt_nocut = opt.dup; opt_nocut.cut_exp = Float::INFINITY
290
- opt.smith_max = smith_search(para, target, opt.armor_min, opt.comp_min, opt: opt_nocut) rescue ( return search_aonly(para, target, opt:) )
291
- opt.armor_max = armor_search(para, target, opt.smith_min, opt.comp_min, opt: opt_nocut) rescue ( return search_sonly(para, target, opt:) )
345
+ opt.smith_max = smith_search(para, target, opt.armor_min, opt.comp_min, opt: opt_nocut) rescue ( opt.smith_max )
346
+ opt.armor_max = armor_search(para, target, opt.smith_min, opt.comp_min, opt: opt_nocut) rescue ( opt.armor_max )
292
347
  opt.smith_min = smith_search(para, target, opt.armor_max, opt.comp_max, opt: opt_nocut)
293
348
  opt.armor_min = armor_search(para, target, opt.smith_max, opt.comp_max, opt: opt_nocut)
294
349
  raise Mgmg::SearchCutException if opt.cut_exp < Mgmg.exp(opt.smith_min, opt.armor_min, opt.comp_min)
295
350
  opt.comp_max = comp_search(para, target, opt.smith_min, opt.armor_min, opt:)
296
351
  exp = Mgmg.exp(opt.smith_min, opt.armor_min, opt.comp_max)
297
352
  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
353
+ eo = opt.irep.eo_para(para)
354
+ comps = Mgmg.fib_init(opt.comp_min, opt.comp_max)
355
+ values = comps.map do |comp|
356
+ r, e = eval_comp_sa(para, target, comp, opt_nocut, eo)
357
+ opt.cut_exp, ret = e, r if e < opt.cut_exp
358
+ e
359
+ end
360
+ while 3 < comps[3]-comps[0]
361
+ if values[1] <= values[2]
362
+ comp = comps[0] + comps[2]-comps[1]
363
+ comps = [comps[0], comp, comps[1], comps[2]]
364
+ r, e = eval_comp_sa(para, target, comp, opt_nocut, eo)
365
+ opt.cut_exp, ret = e, r if e < opt.cut_exp
366
+ values = [values[0], e, values[1], values[2]]
367
+ else
368
+ comp = comps[1] + comps[3]-comps[2]
369
+ comps = [comps[1], comps[2], comp, comps[3]]
370
+ r, e = eval_comp_sa(para, target, comp, opt_nocut, eo)
371
+ opt.cut_exp, ret = e, r if e < opt.cut_exp
372
+ values = [values[1], values[2], e, values[3]]
308
373
  end
374
+ end
375
+ exp_best = opt.cut_exp
376
+ diff = values.max-values.min
377
+ opt.cut_exp = exp_best + diff*opt.fib_ext[0]
378
+ (comps[0]-1).downto(opt.comp_min) do |comp|
379
+ exp_best, ret = fine_sa(exp_best, ret, para, target, comp, opt, eo)
309
380
  rescue Mgmg::SearchCutException
381
+ break
382
+ end
383
+ (comps[3]+1).upto(opt.comp_max) do |comp|
384
+ exp_best, ret = fine_sa(exp_best, ret, para, target, comp, opt, eo)
385
+ rescue Mgmg::SearchCutException
386
+ break
310
387
  end
311
388
  if ret.nil?
312
389
  max = opt.irep.para_call(para, *find_max(para, opt.cut_exp, opt:))
@@ -327,25 +404,107 @@ module Enumerable
327
404
  end
328
405
  smith
329
406
  end
407
+ private def eval_arm(para, armor, comp, eo, opt, ret, max, max_exp)
408
+ smith = Mgmg.invexp3(max_exp, armor, comp)
409
+ exp = Mgmg.exp(smith, armor, comp)
410
+ return [-Float::INFINITY, ret, max] if max_exp < exp
411
+ cur = opt.irep.para_call(para, smith, armor, comp)
412
+ smith = minimize_smith(para, smith, armor, comp, cur, opt) if max <= cur
413
+ ret, max = [smith, armor, comp], cur if ( max < cur || ( max == cur && exp < Mgmg.exp(*ret) ) )
414
+ [cur, ret, max]
415
+ end
416
+ private def eval_comp_fm(para, comp, eo, opt, ret, max, max_exp)
417
+ return [-Float::INFINITY, ret, max] if (comp < opt.comp_min or opt.comp_max < comp)
418
+ comp -= 1 if ( opt.comp_min<comp and ( eo & (2**(comp&1)) == 0 ) )
419
+ cur = -Float::INFINITY
420
+ a_max = [opt.armor_max, Mgmg.invexp3(max_exp, opt.smith_min, comp)].min
421
+ arms = Mgmg.fib_init(opt.armor_min, a_max)
422
+ a_vals = arms.map do |armor|
423
+ cur_i, ret, max = eval_arm(para, armor, comp, eo, opt, ret, max, max_exp)
424
+ cur_i
425
+ end
426
+ while 3 < arms[3]-arms[0]
427
+ if a_vals[2] <= a_vals[1]
428
+ armor = arms[0] + arms[2]-arms[1]
429
+ arms = [arms[0], armor, arms[1], arms[2]]
430
+ cur_i, ret, max = eval_arm(para, armor, comp, eo, opt, ret, max, max_exp)
431
+ a_vals = [a_vals[0], cur_i, a_vals[1], a_vals[2]]
432
+ cur = cur_i if cur < cur_i
433
+ else
434
+ armor = arms[1] + arms[3]-arms[2]
435
+ arms = [arms[1], arms[2], armor, arms[3]]
436
+ cur_i, ret, max = eval_arm(para, armor, comp, eo, opt, ret, max, max_exp)
437
+ a_vals = [a_vals[1], a_vals[2], cur_i, a_vals[3]]
438
+ cur = cur_i if cur < cur_i
439
+ end
440
+ end
441
+ diff = a_vals.max-a_vals.min
442
+ th = max - diff*opt.fib_ext[1]
443
+ (arms[0]-1).downto(opt.armor_min) do |armor|
444
+ cur_i, ret, max = eval_arm(para, armor, comp, eo, opt, ret, max, max_exp)
445
+ break if cur_i < th
446
+ cur = cur_i if cur < cur_i
447
+ end
448
+ (arms[3]+1).upto(a_max) do |armor|
449
+ cur_i, ret, max = eval_arm(para, armor, comp, eo, opt, ret, max, max_exp)
450
+ break if cur_i < th
451
+ cur = cur_i if cur < cur_i
452
+ end
453
+ [cur, ret, max]
454
+ end
330
455
  def find_max(para, max_exp, opt: Mgmg::Option.new)
331
456
  opt = opt.dup.set_default(self)
332
457
  exp = Mgmg.exp(opt.smith_min, opt.armor_min, opt.comp_min)
333
458
  raise Mgmg::SearchCutException, "the recipe requires #{exp.comma3} experiment points, which exceeds given max_exp=#{max_exp.comma3}" if max_exp < exp
334
459
  ret = [Mgmg.invexp3(max_exp, opt.armor_min, opt.comp_min), opt.armor_min, opt.comp_min]
335
460
  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
461
+ eo = opt.irep.eo_para(para)
462
+ opt.comp_max = [opt.comp_max, Mgmg.invexp3c(max_exp, opt.smith_min, opt.armor_min)].min
463
+ comps = Mgmg.fib_init(opt.comp_min, opt.comp_max)
464
+ values = comps.map do |comp|
465
+ cur, ret, max = eval_comp_fm(para, comp, eo, opt, ret, max, max_exp)
466
+ cur
467
+ end
468
+ while 3 < comps[3]-comps[0]
469
+ if values[2] <= values[1]
470
+ comp = comps[0] + comps[2]-comps[1]
471
+ comps = [comps[0], comp, comps[1], comps[2]]
472
+ cur, ret, max = eval_comp_fm(para, comp, eo, opt, ret, max, max_exp)
473
+ values = [values[0], cur, values[1], values[2]]
474
+ else
475
+ comp = comps[1] + comps[3]-comps[2]
476
+ comps = [comps[1], comps[2], comp, comps[3]]
477
+ cur, ret, max = eval_comp_fm(para, comp, eo, opt, ret, max, max_exp)
478
+ values = [values[1], values[2], cur, values[3]]
343
479
  end
344
480
  end
481
+ diff = values.max-values.min
482
+ th = max - diff*opt.fib_ext[1]
483
+ (comps[0]-1).downto(opt.comp_min) do |comp|
484
+ next if ( eo & (2**(comp&1)) == 0 )
485
+ cur, ret, max = eval_comp_fm(para, comp, eo, opt, ret, max, max_exp)
486
+ break if cur < th
487
+ end
488
+ (comps[3]+1).upto(opt.comp_max) do |comp|
489
+ next if ( eo & (2**(comp&1)) == 0 )
490
+ cur, ret, max = eval_comp_fm(para, comp, eo, opt, ret, max, max_exp)
491
+ break if cur < th
492
+ end
345
493
  ret
346
494
  end
347
495
  end
348
496
 
497
+ class << Mgmg
498
+ def fib_init(min, max)
499
+ z = min-1
500
+ a, b = 2, 3
501
+ while z + b < max do
502
+ a, b = b, a+b
503
+ end
504
+ [z, z+b-a, z+a, z+b]
505
+ end
506
+ end
507
+
349
508
  module Mgmg
350
509
  Eighth = 1.quo(8)
351
510
  module_function def find_lowerbound(a, b, para, start, term, opt_a: Option.new, opt_b: Option.new)
@@ -382,28 +541,27 @@ module Mgmg
382
541
  loop do
383
542
  foo = a.find_max(para, eb, opt: opt_a)
384
543
  break if sca == foo
385
- sca, pa = foo, opt_a.irep.para_call(para, *foo)
544
+ bar = opt_a.irep.para_call(para, *foo)
545
+ break if bar < pa
546
+ sca, pa = foo, bar
386
547
  scb = b.search(para, pa, opt: opt_b)
387
548
  foo = Mgmg.exp(*scb)
388
549
  break if eb == foo
389
550
  eb = foo
390
551
  end
391
552
  ea = Mgmg.exp(*sca)
392
- while ea<=eb
393
- tag = pa + Eighth
394
- raise Mgmg::SearchCutException, "given recipes are never reversed from start target=#{start.comma3} until term target=#{term.comma3}" if term < tag
395
- sca, scb = a.search(para, tag, opt: opt_a), b.search(para, tag, opt: opt_b)
396
- ea, eb = Mgmg.exp(*sca), Mgmg.exp(*scb)
397
- pa, pb = opt_a.irep.para_call(para, *sca), opt_b.irep.para_call(para, *scb)
398
- break if ea == eb && pa < pb
399
- end
400
- if eb < ea || ( ea == eb && pa < pb )
553
+ if (eb <= ea and pa <= pb and (eb+pa)!=(ea+pb)) or (eb < ea and sca == a.search(para, pb, opt: opt_a)) then
401
554
  until ea < eb || ( ea == eb && pb < pa )
402
555
  sca = a.find_max(para, ea-1, opt: opt_a)
403
556
  ea, pa = Mgmg.exp(*sca), opt_a.irep.para_call(para, *sca)
404
557
  end
405
558
  return [pa, pb]
406
559
  end
560
+ tag = pa + Eighth
561
+ raise Mgmg::SearchCutException, "given recipes are never reversed from start target=#{start.comma3} until term target=#{term.comma3}" if term < tag
562
+ sca, scb = a.search(para, tag, opt: opt_a), b.search(para, tag, opt: opt_b)
563
+ ea, eb = Mgmg.exp(*sca), Mgmg.exp(*scb)
564
+ pa, pb = opt_a.irep.para_call(para, *sca), opt_b.irep.para_call(para, *scb)
407
565
  end
408
566
  raise UnexpectedError
409
567
  end
@@ -430,7 +588,6 @@ module Mgmg
430
588
  raise Mgmg::SearchCutException, "given recipes are equivalent at start target=#{start.comma3}"
431
589
  end
432
590
 
433
-
434
591
  loop do
435
592
  loop do
436
593
  foo = a.find_max(para, eb, opt: opt_a)
@@ -444,38 +601,40 @@ module Mgmg
444
601
  eb = foo
445
602
  end
446
603
  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
604
+ pb = opt_b.irep.para_call(para, *scb)
605
+ if ea <= eb and pb <= pa and (ea+pb)!=(eb+pa) then
606
+ until pa < pb
607
+ scb = b.search(para, pb+Eighth, opt: opt_b)
608
+ pb = opt_b.irep.para_call(para, *scb)
453
609
  end
610
+ return [pa, pb]
611
+ elsif ea < eb
612
+ return [pa, pb] if scb == b.search(para, pa, opt: opt_b)
613
+ end
614
+ tag = [ea, eb].min - 1
615
+ begin
616
+ scb = b.find_max(para, tag, opt: opt_b)
617
+ rescue Mgmg::SearchCutException
618
+ eb, pb = Mgmg.exp(*scb), opt_b.irep.para_call(para, *scb)
454
619
  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)
620
+ sca = a.find_max(para, eb, opt: opt_a)
621
+ ea, pa = Mgmg.exp(*sca), opt_a.irep.para_call(para, *sca)
622
+ while eb <= ea
623
+ sca = a.find_max(para, ea-1, opt: opt_a)
624
+ ea, pa = Mgmg.exp(*sca), opt_a.irep.para_call(para, *sca)
465
625
  end
466
- return [pa, pb]
467
- elsif res == 2
626
+ rescue Mgmg::SearchCutException
468
627
  raise Mgmg::SearchCutException, "given recipes are never reversed from the start target=#{start.comma3} until #{pa.comma3}"
469
628
  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
629
  return [pa, pb]
478
630
  end
631
+ begin
632
+ sca = a.find_max(para, tag, opt: opt_a)
633
+ rescue Mgmg::SearchCutException
634
+ raise Mgmg::SearchCutException, "given recipes are never reversed from the start target=#{start.comma3} until #{opt_a.irep.para_call(para, *sca).comma3}"
635
+ end
636
+ ea, eb = Mgmg.exp(*sca), Mgmg.exp(*scb)
637
+ pa, pb = opt_a.irep.para_call(para, *sca), opt_b.irep.para_call(para, *scb)
479
638
  end
480
639
  raise UnexpectedError
481
640
  end
data/lib/mgmg/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Mgmg
2
- VERSION = "1.6.2"
2
+ VERSION = "1.8.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`,及びフィボナッチ探索で得られた最後の4点の経験値の最大値`exp2`に対して,`exp+(exp2-exp)*opt.fib_ext[0]`を超えない範囲を探索します.この目的関数は単峰ではないため,フィボナッチ探索のみでは最適解が得られないことがあります.
137
134
 
138
- `opt`は,`String#smith_search`または`String#comp_search`で使われるすべてのパラメータを使用します.
135
+ その他は`String#smith_seach`と同様で,`opt`は,`String#smith_search`または`String#comp_search`で使われるすべてのパラメータと,`fib_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`,及びフィボナッチ探索で得られた最後の4点の`para`値の最小値`p_min`に対して,`p_c-(p_c-p_min)*opt.fib_ext[1]`を下回らない範囲を探索します.
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|`100_000`|対応|各製作Lvの探索範囲の最大値|`String#search`など|
458
+ |fib_ext|`[4, 10]`|対応|フィボナッチ探索後に追加探索を行う範囲|`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.2
4
+ version: 1.8.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-30 00:00:00.000000000 Z
11
+ date: 2022-11-14 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.