mgmg 1.8.0 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 79211ca6cd5422500f7dc957eadfb2c1812459c98fd39b84fc9a3ebf178174ec
4
- data.tar.gz: efa0254194363114aee22993e78cab9359e9a7c92f454d5fffc12b1d14368e6c
3
+ metadata.gz: 3698312de546d3f9a7eac1aadd6024ae8132a8c58f97c041fa07b788828a3bcc
4
+ data.tar.gz: 19d8263ccfd11a476472868a8420959cccc356bbd7f69fcf619f831fde589794
5
5
  SHA512:
6
- metadata.gz: 822a754eeeec7b9b9e8d6f1497041c153a56b62d190da8101f798e9b78334cc64406f35167623387c346b9f2453027a7e17a3ebaefc2ec4d38cbfcbe381b01e6
7
- data.tar.gz: f43028c621ea4025123c1ca338f46c8965d545a8f6a92569cc5bb7f5279d11ea01be0dbe6453c1ffe06c831c55be1cdf439ed66b81fa8e08a4d01ac994348e5f
6
+ metadata.gz: c0edb6042eb1fccbe9d6b4761afd1a2ad0814910850b2664325d20f109fe9d6805cf28aab5d25b363842cde62dce3e1739319668446e912b6a15d94e2043654f
7
+ data.tar.gz: 70ed1f01ca7bfd7ef4be9fc886f74f2049d466cc73e2fb79276dd9768d40bed0232119cde2a177def7721f677271ff602b71d3231d207a94a5850fcd82844b8a
data/CHANGELOG.md CHANGED
@@ -189,3 +189,8 @@
189
189
  - オプション`comp_ext`を`fib_ext`に変更し,追加探索の範囲を修正.
190
190
  - オプション`smith/armor/comp_max`の扱い方を修正し,デフォルト値を10^5に変更.
191
191
  - `Enumerable#find_max`,`Mgmg.#find_lowerbound`のバグを修正.
192
+
193
+ ## 1.8.1 2022/11/19
194
+ - `Mgmg::Recipe`に`name`属性を追加.`String/Enumerable#to_recipe`の際,キーワード引数`name`を追加することで設定できる.
195
+ - `Mgmg.#efficient_list` を追加.
196
+ - `Enumerable#build`,`Enumerable#search`で意図せぬ例外が発生していた問題を修正.
data/README.md CHANGED
@@ -132,6 +132,25 @@ puts r.build(*sc, buff: []) # 一時的に強化を解除して素の性能を
132
132
  #=> 重鎧8☆20(綿宝)[物防:32,538, 魔防:4]
133
133
  ```
134
134
 
135
+ 連続して探索し,最適レシピの推移をcsvファイルに出力する.また,探索範囲内で少なくとも一度は最適に選ばれたレシピのリストを確認する.
136
+
137
+ ```ruby
138
+ str = <<EOS
139
+ 斧(火玉5綿1)+[杖(水1綿1)+[斧(玉5水1)+[杖(綿1綿1)+[斧(玉5綿1)+[剣(金3牙1)+[斧(木2牙1)+[剣(木2牙1)+双短剣(鉄10皮1)]]]]]]] 鍛冶Lv66
140
+ 斧(火玉5綿1)+[杖(鉄2綿1)+[斧(木1金3)+[杖(綿1綿1)+[斧(玉5綿1)+[剣(金3牙1)+[双短剣(金3牙1)+[斧(木2牙1)+剣(鉄10木1)]]]]]]] 鍛冶Lv94
141
+ 斧(木2皮1)+[杖(鉄2綿1)+[斧(木1金3)+[杖(綿1綿1)+[斧(玉5玉5)+[剣(金3牙1)+[双短剣(金3牙1)+[斧(木2牙1)+剣(鉄10木1)]]]]]]] 鍛冶Lv106
142
+ 斧(木2皮1)+[杖(鉄2綿1)+[斧(玉5金3)+[杖(綿1綿1)+[斧(玉5金3)+[剣(金3牙1)+[斧(木2牙1)+[剣(木2牙1)+双短剣(鉄10木1)]]]]]]] 鍛冶Lv116
143
+ 斧(金3水1)+[杖(水1綿1)+[斧(金3水1)+[杖(綿1綿1)+[斧(玉5金3)+[剣(金3牙1)+[双短剣(金3牙1)+[斧(木2牙1)+剣(鉄10木1)]]]]]]] 鍛冶Lv152
144
+ EOS
145
+ rs = str.split(/\n/).map do |line|
146
+ ary = line.split(/ /)
147
+ ary[0].to_recipe(:atkstr, target_weight: 9, name: ary[1])
148
+ end
149
+ res = Mgmg.efficient_list(rs, 20_000, 50_000, './csv.csv', external_encoding: 'Windows-31J')
150
+ puts res.map(&:name).join(', ')
151
+ #=> 鍛冶Lv106, 鍛冶Lv94, 鍛冶Lv116
152
+ ```
153
+
135
154
  各メソッドの詳しい説明等は [リファレンス](./reference.md) を参照されたい.
136
155
 
137
156
  ### 表記ゆれについて
data/lib/mgmg/recipe.rb CHANGED
@@ -1,14 +1,23 @@
1
1
  module Mgmg
2
2
  class Recipe
3
- def initialize(recipe, para=:power, **kw)
3
+ def initialize(recipe, para=:power, name: nil, **kw)
4
4
  @recipe = recipe
5
5
  @recipe.each(&:freeze) if @recipe.kind_of?(Enumerable)
6
6
  @recipe.freeze
7
7
  @para = para
8
+ if name.nil?
9
+ if recipe.kind_of?(String)
10
+ @name = recipe
11
+ else
12
+ @name = recipe.join(' ')
13
+ end
14
+ else
15
+ @name = name
16
+ end
8
17
  @option = Option.new(**kw).set_default(@recipe)
9
18
  end
10
19
  attr_reader :recipe
11
- attr_accessor :para
20
+ attr_accessor :para, :name
12
21
  def initialize_copy(other)
13
22
  @recipe = other.recipe.dup
14
23
  @option = other.option.dup
@@ -82,9 +91,9 @@ module Mgmg
82
91
  smith, armor, comp = opt.smith_min, opt.armor_min, opt.comp_min if smith.nil?
83
92
  case @recipe
84
93
  when String
85
- recipe.show(smith, comp, para:, opt:)
94
+ @recipe.show(smith, comp, para:, name: @name, opt:)
86
95
  when Enumerable
87
- recipe.show(smith, armor, comp, para:, opt:)
96
+ @recipe.show(smith, armor, comp, para:, name: @name, opt:)
88
97
  else
89
98
  raise BrokenRecipeError
90
99
  end
data/lib/mgmg/search.rb CHANGED
@@ -110,16 +110,18 @@ class String
110
110
  end
111
111
  exp_best = opt.cut_exp
112
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)
121
- rescue Mgmg::SearchCutException
122
- break
113
+ if 0 < diff
114
+ opt.cut_exp = exp_best + diff*opt.fib_ext[0]
115
+ (comps[0]-1).downto(opt.comp_min) do |comp|
116
+ exp_best, ret = fine(exp_best, ret, para, target, comp, opt, eo)
117
+ rescue Mgmg::SearchCutException
118
+ break
119
+ end
120
+ (comps[3]+1).upto(opt.comp_max) do |comp|
121
+ exp_best, ret = fine(exp_best, ret, para, target, comp, opt, eo)
122
+ rescue Mgmg::SearchCutException
123
+ break
124
+ end
123
125
  end
124
126
  if ret.nil?
125
127
  max = opt.irep.para_call(para, *find_max(para, opt.cut_exp, opt:))
@@ -177,18 +179,20 @@ class String
177
179
  end
178
180
  end
179
181
  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)
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
182
+ if 0 < diff
183
+ th = max - diff*opt.fib_ext[1]
184
+ (comps[0]-1).downto(opt.comp_min) do |comp|
185
+ next if ( eo & (2**(comp&1)) == 0 )
186
+ cur, smith = eval_comp_fm(para, comp, eo, opt, max, max_exp)
187
+ ret, max = [smith, comp], cur if ( max < cur || ( max == cur && Mgmg.exp(smith, comp) < Mgmg.exp(*ret) ) )
188
+ break if cur < th
189
+ end
190
+ (comps[3]+1).upto(opt.comp_max) do |comp|
191
+ next if ( eo & (2**(comp&1)) == 0 )
192
+ cur, smith = eval_comp_fm(para, comp, eo, opt, max, max_exp)
193
+ ret, max = [smith, comp], cur if ( max < cur || ( max == cur && Mgmg.exp(smith, comp) < Mgmg.exp(*ret) ) )
194
+ break if cur < th
195
+ end
192
196
  end
193
197
  ret
194
198
  end
@@ -342,12 +346,12 @@ module Enumerable
342
346
  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
347
  end
344
348
  opt_nocut = opt.dup; opt_nocut.cut_exp = Float::INFINITY
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 )
349
+ opt.smith_max = ( smith_search(para, target, opt.armor_min, opt.comp_min, opt: opt_nocut) rescue opt.smith_max )
350
+ opt.armor_max = ( armor_search(para, target, opt.smith_min, opt.comp_min, opt: opt_nocut) rescue opt.armor_max )
347
351
  opt.smith_min = smith_search(para, target, opt.armor_max, opt.comp_max, opt: opt_nocut)
348
352
  opt.armor_min = armor_search(para, target, opt.smith_max, opt.comp_max, opt: opt_nocut)
349
353
  raise Mgmg::SearchCutException if opt.cut_exp < Mgmg.exp(opt.smith_min, opt.armor_min, opt.comp_min)
350
- opt.comp_max = comp_search(para, target, opt.smith_min, opt.armor_min, opt:)
354
+ opt.comp_max = ( comp_search(para, target, opt.smith_min, opt.armor_min, opt:) rescue opt.comp_max )
351
355
  exp = Mgmg.exp(opt.smith_min, opt.armor_min, opt.comp_max)
352
356
  opt.cut_exp, ret = exp, [opt.smith_min, opt.armor_min,opt. comp_max] if exp <= opt.cut_exp
353
357
  eo = opt.irep.eo_para(para)
@@ -374,16 +378,18 @@ module Enumerable
374
378
  end
375
379
  exp_best = opt.cut_exp
376
380
  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)
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
381
+ if 0 < diff
382
+ opt.cut_exp = exp_best + diff*opt.fib_ext[0]
383
+ (comps[0]-1).downto(opt.comp_min) do |comp|
384
+ exp_best, ret = fine_sa(exp_best, ret, para, target, comp, opt, eo)
385
+ rescue Mgmg::SearchCutException
386
+ break
387
+ end
388
+ (comps[3]+1).upto(opt.comp_max) do |comp|
389
+ exp_best, ret = fine_sa(exp_best, ret, para, target, comp, opt, eo)
390
+ rescue Mgmg::SearchCutException
391
+ break
392
+ end
387
393
  end
388
394
  if ret.nil?
389
395
  max = opt.irep.para_call(para, *find_max(para, opt.cut_exp, opt:))
@@ -439,16 +445,18 @@ module Enumerable
439
445
  end
440
446
  end
441
447
  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
448
+ if 0 < diff
449
+ th = max - diff*opt.fib_ext[1]
450
+ (arms[0]-1).downto(opt.armor_min) do |armor|
451
+ cur_i, ret, max = eval_arm(para, armor, comp, eo, opt, ret, max, max_exp)
452
+ break if cur_i < th
453
+ cur = cur_i if cur < cur_i
454
+ end
455
+ (arms[3]+1).upto(a_max) do |armor|
456
+ cur_i, ret, max = eval_arm(para, armor, comp, eo, opt, ret, max, max_exp)
457
+ break if cur_i < th
458
+ cur = cur_i if cur < cur_i
459
+ end
452
460
  end
453
461
  [cur, ret, max]
454
462
  end
@@ -479,16 +487,18 @@ module Enumerable
479
487
  end
480
488
  end
481
489
  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
490
+ if 0 < diff
491
+ th = max - diff*opt.fib_ext[1]
492
+ (comps[0]-1).downto(opt.comp_min) do |comp|
493
+ next if ( eo & (2**(comp&1)) == 0 )
494
+ cur, ret, max = eval_comp_fm(para, comp, eo, opt, ret, max, max_exp)
495
+ break if cur < th
496
+ end
497
+ (comps[3]+1).upto(opt.comp_max) do |comp|
498
+ next if ( eo & (2**(comp&1)) == 0 )
499
+ cur, ret, max = eval_comp_fm(para, comp, eo, opt, ret, max, max_exp)
500
+ break if cur < th
501
+ end
492
502
  end
493
503
  ret
494
504
  end
@@ -669,4 +679,81 @@ module Mgmg
669
679
  pa, pb = opt_a.irep.para_call(para, *sca), opt_b.irep.para_call(para, *scb)
670
680
  [sca, ea, pa, scb, eb, pb]
671
681
  end
682
+
683
+ class ELItem
684
+ def initialize(recipe=nil, sc=nil)
685
+ if recipe.nil?
686
+ @para = -Float::INFINITY
687
+ @exp = Float::INFINITY
688
+ else
689
+ @recipe = recipe
690
+ if sc.size == 3
691
+ @smith, @armor, @comp = *sc
692
+ else
693
+ if recipe.option.irep.kind < 8
694
+ @smith, @comp = *sc
695
+ @armor = -1
696
+ else
697
+ @armor, @comp = *sc
698
+ @smith = -1
699
+ end
700
+ end
701
+ @para = recipe.para_call(*sc)
702
+ @exp = Mgmg.exp(*sc)
703
+ @name = recipe.name
704
+ end
705
+ end
706
+ attr_reader :recipe, :smith, :armor, :comp, :para, :exp, :name
707
+ %i|attack phydef magdef hp mp str dex speed magic atkstr atk_sd dex_as mag_das magic2 magmag pmdef hs|.each do |sym|
708
+ define_method(sym) do
709
+ @recipe.para_call(@smith, @armor, @comp, para: sym)
710
+ end
711
+ end
712
+ def weight
713
+ @recipe.build(@smith, @armor, @comp).weight
714
+ end
715
+ end
716
+ private_module_function def _el_sub(f, recipes, start, term, params, header, separator)
717
+ tag, ret = start, []
718
+ f.puts params.join(separator) if header && !f.nil?
719
+ while tag < term
720
+ best = ELItem.new()
721
+ recipes.each do |r|
722
+ cur = ELItem.new(r, r.search(tag))
723
+ if cur.exp < best.exp
724
+ best = cur
725
+ elsif cur.exp == best.exp
726
+ if best.para < cur.para
727
+ best = cur
728
+ elsif best.para == cur.para
729
+ if block_given?
730
+ best = cur if yield(best, cur)
731
+ end
732
+ end
733
+ end
734
+ end
735
+ f.puts( params.map do |sym|
736
+ best.method(sym).call
737
+ end.join(separator) ) unless f.nil?
738
+ ret << best.recipe unless ret.include?(best.recipe)
739
+ tag = best.para+Eighth
740
+ end
741
+ ret
742
+ end
743
+ module_function def efficient_list(recipes, start, term, out=nil, params=[:defaults], separator: ',', header: true, **kw)
744
+ i = params.index(:defaults)
745
+ if i
746
+ params[i] = [:smith, :armor, :comp, :exp, :para, :name]
747
+ params.flatten!
748
+ end
749
+ ret = nil
750
+ if out.kind_of?(String)
751
+ File.open(out, 'w', **kw) do |f|
752
+ ret = _el_sub(f, recipes, start, term, params, header, separator)
753
+ end
754
+ else
755
+ ret = _el_sub(nil, recipes, start, term, params, header, separator)
756
+ end
757
+ ret
758
+ end
672
759
  end
data/lib/mgmg/utils.rb CHANGED
@@ -1,5 +1,13 @@
1
1
  module Mgmg
2
2
  module Refiner
3
+ refine Module do
4
+ private def private_module_function(sym)
5
+ module_function(sym)
6
+ singleton_class.instance_eval do
7
+ private(sym)
8
+ end
9
+ end
10
+ end
3
11
  refine Integer do
4
12
  def comma3
5
13
  self.to_s.gsub(/(\d)(?=(\d{3})+(?!\d))/, '\1,')
data/lib/mgmg/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Mgmg
2
- VERSION = "1.8.0"
2
+ VERSION = "1.8.1"
3
3
  end
data/lib/mgmg.rb CHANGED
@@ -117,18 +117,19 @@ class String
117
117
  def peff(para, smith, comp=smith, opt: Mgmg::Option.new)
118
118
  poly(para, opt:).eff(smith, comp)
119
119
  end
120
- def show(smith=-1, comp=smith, para: :power, opt: Mgmg::Option.new)
120
+ def show(smith=-1, comp=smith, para: :power, name: nil, opt: Mgmg::Option.new)
121
121
  rein = case opt.reinforcement
122
122
  when Array
123
123
  opt.reinforcement.map{|r| Mgmg::Reinforcement.compile(r)}
124
124
  else
125
125
  [Mgmg::Reinforcement.compile(opt.reinforcement)]
126
126
  end
127
+ name = self if name.nil?
127
128
  built = build(smith, comp, opt:)
128
129
  pstr = '%.3f' % built.para_call(para)
129
130
  pstr.sub!(/\.?0+\Z/, '')
130
131
  puts "With levels (#{smith}, #{comp}: #{Mgmg.exp(smith, comp).comma3}), building"
131
- puts " #{self}"
132
+ puts " #{name}"
132
133
  rein = rein.empty? ? '' : "reinforced by {#{rein.join(',')}} "
133
134
  puts "#{rein}yields (#{pstr}, #{built.total_cost})"
134
135
  puts " #{built}"
@@ -155,31 +156,36 @@ module Enumerable
155
156
  opt = opt.dup
156
157
  rein = opt.reinforcement
157
158
  opt.reinforcement = []
158
- self.sum(Mgmg::Equip::Zero) do |str|
159
- if Mgmg::EquipPosition[str.build(opt:).kind] == 0
160
- str.build(smith, comp, opt:)
161
- else
162
- str.build(armor, comp, opt:)
163
- end
164
- end.reinforce(*rein)
159
+ if self.empty?
160
+ Mgmg::Equip::Zero
161
+ else
162
+ self.sum(Mgmg::Equip::Zero) do |str|
163
+ if Mgmg::EquipPosition[str.build(opt:).kind] == 0
164
+ str.build(smith, comp, opt:)
165
+ else
166
+ str.build(armor, comp, opt:)
167
+ end
168
+ end.reinforce(*rein)
169
+ end
165
170
  end
166
171
  def ir(opt: Mgmg::Option.new)
167
172
  self.sum(Mgmg::IR::Zero) do |str|
168
173
  str.ir(opt:)
169
174
  end.add_reinforcement(opt.reinforcement)
170
175
  end
171
- def show(smith=-1, armor=smith, comp=armor.tap{armor=smith}, para: :power, opt: Mgmg::Option.new)
176
+ def show(smith=-1, armor=smith, comp=armor.tap{armor=smith}, para: :power, name: nil, opt: Mgmg::Option.new)
172
177
  rein = case opt.reinforcement
173
178
  when Array
174
179
  opt.reinforcement.map{|r| Mgmg::Reinforcement.compile(r)}
175
180
  else
176
181
  [Mgmg::Reinforcement.compile(opt.reinforcement)]
177
182
  end
183
+ name = self.join(' ') if name.nil?
178
184
  built = self.build(smith, armor, comp, opt:)
179
185
  pstr = '%.3f' % built.para_call(para)
180
186
  pstr.sub!(/\.?0+\Z/, '')
181
187
  puts "With levels (#{smith}, #{armor}, #{comp}: #{Mgmg.exp(smith, armor, comp).comma3}), building"
182
- puts " #{self.join(', ')}"
188
+ puts " #{name}"
183
189
  rein = rein.empty? ? '' : "reinforced by {#{rein.join(',')}} "
184
190
  puts "#{rein}yields (#{pstr}, #{built.total_cost})"
185
191
  puts " #{built}"
data/reference.md CHANGED
@@ -1,11 +1,13 @@
1
1
  # リファレンス
2
2
  本ライブラリで定義される主要なメソッドを以下に解説します.
3
3
 
4
- ## `String#to_recipe(para=:power, allow_over20: false, **kw)`,`Enumerable#to_recipe(para=:power, allow_over20: false, **kw)`
4
+ ## `String#to_recipe(para=:power, allow_over20: false, name: nil, **kw)`,`Enumerable#to_recipe(para=:power, allow_over20: false, name: nil, **kw)`
5
5
  レシピ文字列である`self`と,注目パラメータ`para`,オプション`Mgmg.option(**kw)`をセットにした[後述](#mgmgrecipe)の`Mgmg::Recipe`オブジェクトを生成して返します.デフォルト値としてセットされたパラメータを使用する以外は,レシピ文字列と同様に扱えます.
6
6
 
7
7
  `allow_over20`が偽の場合,レシピの☆を確認し,20を超える場合は,例外`Mgmg::Over20Error`を発生します.このチェックを抑制したい場合は,真にしてください.
8
8
 
9
+ `name`は,一部のメソッドでレシピ文字列の代わりに使われる文字列を指定します.デフォルト値の`nil`の場合,`String`なら`self`,`Enumerable`なら`self.join(' ')`になります.
10
+
9
11
  ## `String#build(smith=-1, comp=smith, opt: Mgmg.option())`
10
12
  レシピ文字列である`self`を解釈し,鍛冶・防具製作Lvを`smith`,道具製作Lvを`comp`として鍛冶・防具製作及び武器・防具合成を行った結果を[後述](#mgmgequip)の`Mgmg::Equip`クラスのインスタンスとして生成し,返します.例えば,
11
13
  ```ruby
@@ -108,7 +110,7 @@
108
110
 
109
111
  `opt`は,`left_associative`と`include_system_equips`,`reinforcement`を使用します.
110
112
 
111
- ## `String#smith_seach(para, target, comp, opt: Mgmg.option())`
113
+ ## `String#smith_search(para, target, comp, opt: Mgmg.option())`
112
114
  `para`の値が`target`以上となるのに必要な最小の鍛冶・防具製作Lvを二分探索で探索して返します.
113
115
  道具製作Lvは`comp`で固定,鍛冶・防具製作Lvを`opt.smith_min`と`opt.smith_max`で挟み込んで探索します.
114
116
 
@@ -123,16 +125,16 @@
123
125
  `opt`は,上記の他に`left_associative`,`reinforcement`,`irep`を使用します.
124
126
 
125
127
  ## `String#comp_search(para, target, smith, opt: Mgmg.option())`
126
- `String#smith_seach`とは逆に,鍛冶・防具製作Lvを固定して最小の道具製作Lvを探索します.
128
+ `String#smith_search`とは逆に,鍛冶・防具製作Lvを固定して最小の道具製作Lvを探索します.
127
129
  探索の起点である`opt.comp_min`のデフォルト値は,製作に必要な最小の道具製作Lv (`self.min_comp`)です.
128
- その他は`String#smith_seach`と同様です.
130
+ その他は`String#smith_search`と同様です.
129
131
 
130
132
  `opt`は,`comp_min`,`comp_max`,`left_associative`,`reinforcement`,`irep`を使用します.
131
133
 
132
134
  ## `String#search(para, target, opt: Mgmg.option())`
133
135
  合計経験値が最小となる鍛冶・防具製作Lvと道具製作Lvの組を探索します.道具製作Lvを決定変数として,`smith_search`の返り値との合計経験値を最小化する道具製作Lvをフィボナッチ探索で探索した後,得られた解 c の前後を探索し,最適化します.このとき,道具製作Lv c における合計経験値`exp`,及びフィボナッチ探索で得られた最後の4点の経験値の最大値`exp2`に対して,`exp+(exp2-exp)*opt.fib_ext[0]`を超えない範囲を探索します.この目的関数は単峰ではないため,フィボナッチ探索のみでは最適解が得られないことがあります.
134
136
 
135
- その他は`String#smith_seach`と同様で,`opt`は,`String#smith_search`または`String#comp_search`で使われるすべてのパラメータと,`fib_ext`を使用します.
137
+ その他は`String#smith_search`と同様で,`opt`は,`String#smith_search`または`String#comp_search`で使われるすべてのパラメータと,`fib_ext`を使用します.
136
138
 
137
139
  ## `Enumerable#search(para, target, opt: Mgmg.option())`
138
140
  複数装備の組について,`para`の値が`target`以上となる最小経験値の`[鍛冶Lv,防具製作Lv,道具製作Lv]`を返します.
@@ -140,7 +142,7 @@
140
142
 
141
143
  `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`です.
142
144
 
143
- その他は,`String#seach`と同様です.
145
+ その他は,`String#search`と同様です.
144
146
 
145
147
  ## `String#find_max(para, max_exp, opt: Mgmg.option())`
146
148
  経験値の合計が`max_exp`以下の範囲で,`para`の値が最大となる鍛冶・防具製作Lvと道具製作Lvからなる配列を返します.`para`の値が最大となる組が複数存在する場合,経験値が小さい方が優先されます.
@@ -165,6 +167,46 @@
165
167
 
166
168
  `opt_a`,`opt_b`は,`Mgmg.#find_lowerbound`と同様です.
167
169
 
170
+ ## `Mgmg.#efficient_list(recipes, start, term, out=nil, params=[:defaults], separator: ',', header: true, **kw){|former, latter| ...}`
171
+ 目標値`start`から`term`までにおいて,最も効率の良いレシピとそのときのpara値を求め,一覧にしたcsvファイルを出力します.
172
+ `recipes`のうち,一度でも最適レシピに選ばれたものを抽出した配列を返します.
173
+
174
+ 具体的には,以下のように動作します.
175
+ `Mgmg::Recipe`の配列`recipes`の各要素`r`に対し,`r.search(start)`を実行し,総経験値が最も少なくなる`r_best`を求めます.
176
+ `r_best`の行を出力した後,目標値を`r_best.para_call(*r_best.search(start))+1.quo(8)`に変更し,目標値が`term`を超えるまで繰り返します.
177
+
178
+ 結果は`out`に出力します.`out`が`String`の場合,ファイルパスとみなし,`File.open(out, 'w', **kw)`に対して出力します.
179
+ `out`が`nil`の場合,csvファイルは出力しません.その他の場合,`out.puts`により,`out`そのものに書き込みます.
180
+
181
+ `params`には,出力したい項目名の`Symbol`を並べた配列を指定します.`:defaults`は,自動的に`:smith, :armor, :comp, :exp, :para, :name`に展開され,
182
+ それぞれ鍛冶Lv,防具製作Lv,道具製作Lv,総経験値,para値,レシピ名です.その他,
183
+ `:weight`, `:attack`, `:phydef`, `:magdef`, `:hp`, `:mp`, `:str`, `:dex`, `:speed`, `:magic`, `:atkstr`, `:atk_sd`, `:dex_as`, `:mag_das`, `:magic2`, `:magmag`, `:pmdef`, `:hs`
184
+ が指定でき,各製作Lvでの個別のパラメータを出力できます.
185
+
186
+ `separator`は,項目間の区切り文字を指定します.`header`が真の場合,1行目に,項目名を並べたヘッダを出力します.不要な場合は偽を指定してください.
187
+ 残りの`kw`は,`File.open(out, 'w', **kw)`にそのまま渡されます.出力結果をExcelで開く場合には`external_encoding: "Windows-31J"`などを指定します.
188
+ `out`が`String`でない場合は,`kw`は無視されます.
189
+
190
+ ブロック付きで呼び出した場合,ブロックの評価でタイブレイクを行います.必要な経験値が同じだった場合,para値が大きい方を優先しますが,para値も同じ場合,
191
+ `recipes`の並びで前の方を`former`,後ろの方を`latter`としてブロックを実行します.`yield(former, latter)`が真なら`latter`を,偽なら`former`を優先します.
192
+ `fomer`,`latter`は,`Mgmg::ELItem`インスタンスであり,`params`に指定できる項目がそのままメソッド名として定義されている他,
193
+ `Mgmg::ELItem#recipe`により,`recipes`の要素として渡したレシピオブジェクトを取り出せます.
194
+ 例えば,双短剣のレシピを比較していて,para値として指定した威力が同じ場合は器用さの高い方を,それも同じ場合は素早さが高い方を優先したい場合,以下のようにします.
195
+
196
+ ```ruby
197
+ Mgmg.efficient_list(recipes, start, term) do |former, latter|
198
+ if former.dex < latter.dex
199
+ true
200
+ elsif former.dex == latter.dex
201
+ former.speed < latter.speed
202
+ else
203
+ false
204
+ end
205
+ end
206
+ ```
207
+
208
+ ブロックを指定していない場合,常に`former`が優先されます.
209
+
168
210
  ## `String#eff(para, smith, comp=smith, opt: Mgmg.option())`
169
211
  [`smith`を1上げたときの`para`値/(`smith`を1上げるのに必要な経験値), `comp`を1上げたときの`para`値/(`comp`を2上げるのに必要な経験値)]を返します.
170
212
  `para`は,`Mgmg::Equip`のメソッド名をシンボルで指定(`:power, :fpower`も可)します.
@@ -438,7 +480,7 @@ alias として`*`があるほか`scalar(1.quo(value))`として`quo`,`/`,`s
438
480
  ### 引数4つ
439
481
  調理法,主食材,副食材,料理Lvを指定し,対応する料理の効果の**概算値**を計算します.計算式は [Wikiの記述](https://wikiwiki.jp/guruguru/%E3%82%A2%E3%82%A4%E3%83%86%E3%83%A0/%E9%A3%9F%E7%B3%A7%E5%93%81#y1576f2d) に基づきますが,正確ではないことがわかっています.例えば,`('蒸す', 'アースドラン', '氷河酒', 27)`の物防は87ですが,この計算式では88になります.調理法,主食材,副食材は文字列で,料理Lvは整数で指定します.
440
482
 
441
- 調理法は「焼き」か「蒸す」,主食材は「獣肉」「ウッチ」「ゴッチ」「ガガッチ」「ドランギョ」「ドラバーン」「フレドラン」「アースドラン」「アクアドラン」「ダークドン」,副食材は「氷酒」「氷水酒」「氷河酒」「カエン酒」「爆炎酒」「煉獄酒」のみ対応しています.攻撃,物防,魔防の強化を考える場合,これらで十分と判断しての選択となっています.なお,副食材の数値は,Wikiの表で順番が間違っていると思われる部分を修正しています.
483
+ 調理法は「焼き」か「蒸す」,主食材は「獣肉」「ウッチ」「ゴッチ」「ガガッチ」「ドランギョ」「ドラバーン」「フレドラン」「アースドラン」「アクアドラン」「ダークドン」,副食材は「氷酒」「氷水酒」「氷河酒」「カエン酒」「爆炎酒」「煉獄酒」のみ対応しています.攻撃,物防,魔防の強化を考える場合,これらで十分と判断しての選択となっています.
442
484
 
443
485
  ## `Mgmg::Option`
444
486
  多くのメソッドのオプション引数をまとめるクラスです.このクラスのインスタンスを使ってそれぞれのメソッドに引き渡します.
@@ -446,7 +488,7 @@ alias として`*`があるほか`scalar(1.quo(value))`として`quo`,`/`,`s
446
488
  ## `Mgmg.#option(recipe=nil, **kw)`
447
489
  `kw`はキーワード引数本体です.定義されているキーワードと意味,使用される主なメソッドは下表の通りです.デフォルト値は簡易的な表示であり,細かい点では不正確です.
448
490
  `recipe`にレシピ`String`または`Enumerable`を渡すと,そのレシピを使ってデフォルト値を具体化しますが,各メソッドで自動的に具体化されるため,通常は必要ありません.
449
- `Defaults`対応が「対応」となっているキーワード引数については,`Mgmg::Option::Defaults[:include_system_equips]=false`などとすることで,デフォルト値をグローバルに変更することができます.デフォルト値にかかわらず,メソッド呼び出し時に個別に指定すればその値が優先されます.
491
+ `Defaults`対応が「対応」となっているキーワード引数については,`Mgmg::Option::Defaults[:include_system_equips]=false`などとすることで,デフォルト値をグローバルに変更することができます.デフォルト値にかかわらず,メソッド呼び出し時に個別に指定すればその値が優先されます.また,デフォルト値を変更しても,すでに生成された`Option`オブジェクトの値は変更されません.
450
492
 
451
493
  |キーワード|デフォルト値|`Defaults`対応|意味|主なメソッド,備考|
452
494
  |:-|:-|:-|:-|:-|
@@ -491,7 +533,7 @@ alias として`*`があるほか`scalar(1.quo(value))`として`quo`,`/`,`s
491
533
  `self.recipe.find_max`を呼び出して返します.注目パラメータはセットされたものを自動的に渡しますが,別のパラメータを使いたい場合,キーワード引数で指定します.その他のキーワード引数を与えた場合,オプションのうち,与えられたパラメータのみ一時的に上書きします.
492
534
 
493
535
  ## `Mgmg::Recipe#min_level(w=self.option.target_weight, include_outsourcing=false)`
494
- `self.recipe.min_level(w, include_outsourcing)`を返します.`w`のデフォルトパラメータが`target_weight`になっている以外は`String#min_level`または`Enumerable#min_level`と同じです.
536
+ `self.recipe.min_level(w, include_outsourcing)`を返します.`w`のデフォルト値が`target_weight`になっている以外は`String#min_level`または`Enumerable#min_level`と同じです.
495
537
 
496
538
  ## `Mgmg::Recipe#min_weight, max_weight, min_levels, min_levels_max, min_smith, min_comp`
497
539
  `self.recipe`の同名メソッドをそのまま呼び出して返します.
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.8.0
4
+ version: 1.8.1
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-14 00:00:00.000000000 Z
11
+ date: 2022-11-19 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.25
107
+ rubygems_version: 3.3.26
108
108
  signing_key:
109
109
  specification_version: 4
110
110
  summary: Calculate specs of equipments of Megurimeguru, a game produced by Kou.