mgmg 1.5.0 → 1.5.3

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: '0939daf2680e3d9a8a80a9616355ed10232ce6794b288ea86d295d283cfde413'
4
- data.tar.gz: 7c6f9fdf112335d9844f1ec9e06c694b9cffd0a897787b4c9e74b306296b179c
3
+ metadata.gz: 38cdc3b73d921d0af024b6a4e52401c2539dd3e06a300116054e6f6b0dca03d7
4
+ data.tar.gz: 4235ea18224761cdeced8d9026b8d27c9690093fb1116a7eb4a36827f3ea3c3c
5
5
  SHA512:
6
- metadata.gz: 31c5e140183a5417c833b7af756b454701333d71293bad1e5cd3f15073d71657c0503038daf0f7c7c02290aee22cef38c795ff98fc92df85462da96759c816f9
7
- data.tar.gz: d02cfe4e1f571b76083c644cd0c44d41caced733e22d4c24853ac91b2a3410f53500c7867bda2b2109d46a873d4110a801d1cf2ecb7f2a3a54ba48f1d31d8b51
6
+ metadata.gz: dac5f9bdd58018092822acba25a1f9e1c965ea6a1d3756b12903c081be4cf6761ba3eabb76326dc4d587f171ece4adc8d861d425368c3090a25c2c5faa036948
7
+ data.tar.gz: bcecf50fa423d798bf74da4f4968054c1200aa31f597750964fa0f34fb53439916bcc455d35f43a82a6e0701e902a4ecf527961eed758d7daacc67c622ccc93d
data/CHANGELOG.md CHANGED
@@ -131,3 +131,16 @@
131
131
  - 関連して,`String#max_weight`,`String#min_weight`,`Enumerable#max_weight`,`Enumerable#min_weight`,`Enumerable#max_weights`,`Enumerable#min_weights`を追加した.
132
132
  - `Mgmg::Option#smith_min`,`Mgmg::Option#armor_min`のデフォルト値を,`String#min_level`,`Enumerable#min_level`を用いて設定する仕様とし,その目標重量を`Mgmg::Option#target_weight`で指定するようにした.
133
133
  - `String#min_comp`,`String#min_smith`,`Enumerable#min_comp`,`Enumerable#min_smith`において,既製品のみである,合成を行わないなど,該当スキルが必要ないレシピである場合の返り値を`-1`に変更した.
134
+
135
+ ## 1.5.1 2022/06/24
136
+ - `Mgmg::Recipe`を実装し,レシピ`String`または`Enumerable`と,注目パラメータ`Symbol`,オプション`Mgmg::Option`をセットで取り扱えるようにした.
137
+ - `String#to_recipe(para=:power, allow_over20: false, **kw)`または`Enumerable#to_recipe(para=:power, **kw)`で生成できる.
138
+ - `Mgmg::Recipe#build`,`Mgmg::Recipe#search`など`String`等に対する操作と同様にでき,注目パラメータとオプションは,`to_recipe`でセットしたものがデフォルトで使われるようになる.各メソッド呼び出し時にキーワード引数を与えることで,一時的に上書きすることもできる.
139
+ - `String#to_recipe`にのみ,☆20制限のチェック機構を導入した.
140
+ - 計算を繰り返した際,複数装備の装備数が増加していってしまうバグを修正した.
141
+
142
+ ## 1.5.2 2022/06/28
143
+ - `String#poly`が`:magic2`に対応していなかったバグを修正.
144
+
145
+ ## 1.5.3 2022/06/28
146
+ - `Recipe#option`がキーワード引数を受け取れなかったバグを修正.
data/README.md CHANGED
@@ -54,6 +54,7 @@ puts r.build(122, 139, 232)
54
54
  ```
55
55
 
56
56
  製作品の重量及び特定の重量にするために必要な鍛冶・防具製作Lvを確認する.
57
+
57
58
  ```ruby
58
59
  r = 'サンダル(骨2皮1)+[重鎧(金3金3)+[フード(骨2宝1)+[重鎧(皮10金10)+軽鎧(鉄10皮1)]]]'
59
60
  p r.min_levels # 各材料装備を重量1で作るのに必要な防具製作Lv
@@ -61,7 +62,7 @@ p r.min_levels # 各材料装備を重量1で作るのに必要
61
62
  p r.min_levels(2) # 同重量2以下
62
63
  #=> {"サンダル(骨2皮1)"=>3, "重鎧(金3金3)"=>102, "フード(骨2宝1)"=>3, "重鎧(皮10金10)"=>42, "軽鎧(鉄10皮1)"=>27}
63
64
  p [r.min_weight, r.max_weight] # 最小重量と最大重量
64
- #=> [5, 11]
65
+ #=> [5, 10]
65
66
  p r.min_level # 最小重量で作るのに必要な防具製作Lv
66
67
  #=> 202
67
68
  p r.min_level(7) # 重量7以下で作るのに必要な防具製作Lv
@@ -70,6 +71,20 @@ p r.min_level(-1) # 最小重量+1=重量6以下で作るのに必
70
71
  #=> 162
71
72
  ```
72
73
 
74
+ 注目パラメータやオプションパラメータを,レシピ文字列とセットにして取り回す.
75
+ ```ruby
76
+ r = '[[斧(鉄10皮1)+剣(牙10皮1)]+剣(鉄10皮1)]+剣(鉄10皮1)'
77
+ r = r.to_recipe(:atkstr, target_weight: 5) # 注目パラメータを攻撃力+腕力,目標重量を5に設定
78
+ p [r.min_level, r.min_level(0)] # 必要な鍛冶Lvのデフォルトが目標重量ベースに
79
+ #=> [72, 168] # 鍛冶Lv72で重量5(目標)に,鍛冶Lv168で重量4(最小)になる
80
+ puts r.build(27, 42) # 変換前と同様に合成等もできる
81
+ #=> 斧8☆20(鉄鉄)[攻撃:1,112, 腕力:18] # 鍛冶Lv27だと重量8に
82
+ p r.para_call(27, 42) # 注目パラメータのみを計算
83
+ #=> 1130 # 1112+18=1130
84
+ puts r.build(nil) # 目標重量を達成する最小製作Lvで製作
85
+ #=> 斧5☆20(鉄鉄)[攻撃:1,436, 腕力:24] # 製作Lvを nil にすると,目標重量のための最小製作Lv (72, 42) で製作
86
+ ```
87
+
73
88
  近似多項式を得る.
74
89
 
75
90
  ```ruby
@@ -97,22 +112,26 @@ p [sc, Mgmg.exp(*sc)]
97
112
 
98
113
  ```ruby
99
114
  r = '双短剣(金3皮1)+[杖(水1綿1)+[斧(玉5水1)+[杖(鉄1綿1)+[[斧(木2金3)+剣(鉄10皮1)]+[剣(木2綿1)+双短剣(鉄10皮1)]]]]]'
100
- sc = r.search(:atk_sd, 2000, opt: Mgmg.option(target_weight: 9))
115
+ r = r.to_recipe(:atk_sd, target_weight: 9)
116
+ sc = r.search(2000) # to_recipe で注目パラメータを入れているので,ここでは不要
101
117
  p [sc, Mgmg.exp(*sc)]
102
- #=> [[70, 130], 38046]
103
- # オプションなしの場合は鍛冶Lv100以上を探索するため,[[100, 126], 41054] になる
118
+ #=> [[70, 130], 38046] # オプションなしの場合は鍛冶Lv100以上を探索するため,[[100, 126], 41054] になる
104
119
  ```
105
120
 
106
121
  探索の際,スキル及び料理を考慮する.
107
122
 
108
123
  ```ruby
109
124
  r = '重鎧(皮2綿1)+[帽子(宝1宝1)+[重鎧(玉5金3)+[帽子(宝1宝1)+[重鎧(玉5金6)+[軽鎧(金3骨1)+[重鎧(皮2骨1)+軽鎧(鉄10綿1)]]]]]]'
110
- sc = r.search(:phydef, 100_000, opt: Mgmg.option(buff: %w|物防御UP アースドランと氷河酒の蒸し焼き ガードアップ|))
125
+ r = r.to_recipe(:phydef, buff: %w|物防御UP アースドランと氷河酒の蒸し焼き ガードアップ|)
126
+ sc = r.search(100_000)
111
127
  p [sc, Mgmg.exp(*sc)]
112
128
  #=> [[120, 264], 152502]
129
+ puts r.build(*sc) # 実際に作って確認
130
+ #=> 重鎧8☆20(綿宝)[物防:100,396.0, 魔防:4]
131
+ puts r.build(*sc, buff: []) # 一時的に強化を解除して素の性能を確認
132
+ #=> 重鎧8☆20(綿宝)[物防:32,538, 魔防:4]
113
133
  ```
114
134
 
115
-
116
135
  各メソッドの詳しい説明等は [リファレンス](./reference.md) を参照されたい.
117
136
 
118
137
  ### 表記ゆれについて
data/lib/mgmg/equip.rb CHANGED
@@ -13,7 +13,7 @@ module Mgmg
13
13
  def initialize_copy(other)
14
14
  @kind = other.kind
15
15
  @weight = other.weight
16
- @star = other.star
16
+ @star = other.star.dup
17
17
  @main = other.main
18
18
  @sub = other.sub
19
19
  @para = other.para.dup
@@ -193,8 +193,8 @@ module Mgmg
193
193
  def +(other)
194
194
  self.dup.add!(other)
195
195
  end
196
- Zero = self.new(28, 0, Vec.new(6, 0), 12, 12, Vec.new(9, 0), Vec.new(3, 0))
197
- Zero.history.clear
196
+ Zero = self.new(28, 0, Vec.new(6, 0).freeze, 12, 12, Vec.new(9, 0).freeze, Vec.new(3, 0).freeze)
197
+ Zero.total_cost.freeze; Zero.history.clear.freeze
198
198
  Zero.freeze
199
199
  end
200
200
 
data/lib/mgmg/ir.rb CHANGED
@@ -152,7 +152,7 @@ module Mgmg
152
152
  attr_accessor :kind, :star, :main, :sub, :para, :rein
153
153
  def initialize_copy(other)
154
154
  @kind = other.kind
155
- @star = other.star
155
+ @star = other.star.dup
156
156
  @main = other.main
157
157
  @sub = other.sub
158
158
  @para = other.para.dup
@@ -313,7 +313,8 @@ module Mgmg
313
313
  def +(other)
314
314
  self.dup.add!(other)
315
315
  end
316
- Zero = self.new(28, Vec.new(6, 0), 12, 12, Array.new(9){Const.new(0)})
316
+ Zero = self.new(28, Vec.new(6, 0).freeze, 12, 12, Array.new(9){Const.new(0)}.freeze)
317
+ Zero.rein.freeze; Zero.freeze
317
318
  end
318
319
  class << IR
319
320
  def build(str, left_associative: true, reinforcement: [])
data/lib/mgmg/optimize.rb CHANGED
@@ -4,9 +4,9 @@ module Mgmg
4
4
  InvList = [%w|帽子 フード サンダル|.freeze, %w|宝1 骨1 木1 木2 骨2|.freeze, %w|宝1 骨1 木1|.freeze].freeze
5
5
  def phydef_optimize(str, smith, comp=smith, opt: Option.new)
6
6
  best = if smith.nil? then
7
- [str, str.poly(:phydef, opt: opt), str.poly(:magdef, opt: opt), str.poly(:cost, opt: opt)]
7
+ [str, str.poly(:phydef, opt:), str.poly(:magdef, opt:), str.poly(:cost, opt:)]
8
8
  else
9
- [str, str.build(smith, comp, opt: opt)]
9
+ [str, str.build(smith, comp, opt:)]
10
10
  end
11
11
  str = Mgmg.check_string(str)
12
12
  ai = 0
@@ -23,7 +23,7 @@ module Mgmg
23
23
  m = /([^\+]*\([^\(]+[綿皮]1\))\]*\Z/.match(str)
24
24
  if m
25
25
  if smith
26
- if m[1].sub(/綿1\)/, '皮1)').build(smith, opt: opt).weight == m[1].sub(/皮1\)/, '綿1)').build(smith, opt: opt).weight
26
+ if m[1].sub(/綿1\)/, '皮1)').build(smith, opt:).weight == m[1].sub(/皮1\)/, '綿1)').build(smith, opt:).weight
27
27
  skin = true
28
28
  end
29
29
  else
@@ -40,9 +40,9 @@ module Mgmg
40
40
  while b
41
41
  r = pd_apply_idx(str, a, b)
42
42
  best = if smith.nil? then
43
- pd_better(best, [r, r.poly(:phydef, opt: opt), r.poly(:magdef, opt: opt), r.poly(:cost, opt: opt)], opt.magdef_maximize)
43
+ pd_better(best, [r, r.poly(:phydef, opt:), r.poly(:magdef, opt:), r.poly(:cost, opt:)], opt.magdef_maximize)
44
44
  else
45
- pd_better(best, [r, r.build(smith, comp, opt: opt)], opt.magdef_maximize)
45
+ pd_better(best, [r, r.build(smith, comp, opt:)], opt.magdef_maximize)
46
46
  end
47
47
  b = pd_next_b(b)
48
48
  end
@@ -58,9 +58,9 @@ module Mgmg
58
58
  while b
59
59
  r = pd_apply_idx(str, a, b)
60
60
  best = if smith.nil? then
61
- pd_better(best, [r, r.poly(:phydef, opt: opt), r.poly(:magdef, opt: opt), r.poly(:cost, opt: opt)], opt.magdef_maximize)
61
+ pd_better(best, [r, r.poly(:phydef, opt:), r.poly(:magdef, opt:), r.poly(:cost, opt:)], opt.magdef_maximize)
62
62
  else
63
- pd_better(best, [r, r.build(smith, comp, opt: opt)], opt.magdef_maximize)
63
+ pd_better(best, [r, r.build(smith, comp, opt:)], opt.magdef_maximize)
64
64
  end
65
65
  b = pd_next_b(b)
66
66
  end
@@ -159,7 +159,7 @@ module Mgmg
159
159
 
160
160
  MwList = %w|綿 皮 骨 木 水|.freeze
161
161
  def buster_optimize(str, smith, comp=smith, opt: Option.new)
162
- best = ( smith.nil? ? [str, str.poly(:mag_das, opt: opt)] : [str, str.build(smith, comp, opt: opt)] )
162
+ best = ( smith.nil? ? [str, str.poly(:mag_das, opt:)] : [str, str.build(smith, comp, opt:)] )
163
163
  str = Mgmg.check_string(str)
164
164
  ai = -1
165
165
  org = nil
@@ -174,7 +174,7 @@ module Mgmg
174
174
  a = Array.new(ai){ [0, 0, 0] }
175
175
  while a
176
176
  r = bus_apply_idx(str, a)
177
- best = bus_better(best, ( smith.nil? ? [r, r.poly(:mag_das, opt: opt)] : [r, r.build(smith, comp, opt: opt)] ))
177
+ best = bus_better(best, ( smith.nil? ? [r, r.poly(:mag_das, opt:)] : [r, r.build(smith, comp, opt:)] ))
178
178
  a = bus_next_a(a)
179
179
  end
180
180
  best[0]
data/lib/mgmg/option.rb CHANGED
@@ -1,9 +1,4 @@
1
1
  module Mgmg
2
- module_function def option(recipe=nil, **kw)
3
- ret = Option.new(**kw)
4
- ret.set_default(recipe) unless recipe.nil?
5
- ret
6
- end
7
2
  class Option
8
3
  def initialize(
9
4
  left_associative: true,
@@ -50,7 +45,7 @@ module Mgmg
50
45
  @irep = other.irep
51
46
  @cut_exp = other.cut_exp
52
47
  end
53
- def set_default(recipe, force: false)
48
+ def update_sa_min(recipe, force=true)
54
49
  case recipe
55
50
  when String
56
51
  if @smith_min.nil? && @armor_min
@@ -71,6 +66,10 @@ module Mgmg
71
66
  else
72
67
  raise ArgumentError, 'recipe should be String or Enumerable'
73
68
  end
69
+ self
70
+ end
71
+ def set_default(recipe, force: false)
72
+ update_sa_min(recipe, force)
74
73
  @comp_min = recipe.min_comp(opt: self) if force || @comp_min.nil?
75
74
  @irep = recipe.ir(opt: self) if force || @irep.nil?
76
75
  self
@@ -0,0 +1,172 @@
1
+ module Mgmg
2
+ class Recipe
3
+ def initialize(recipe, para=:power, **kw)
4
+ @recipe = recipe
5
+ @recipe.each(&:freeze) if @recipe.kind_of?(Enumerable)
6
+ @recipe.freeze
7
+ @para = para
8
+ @option = Option.new(**kw).set_default(@recipe)
9
+ end
10
+ attr_reader :recipe
11
+ attr_accessor :para
12
+ def initialize_copy(other)
13
+ @recipe = other.recipe.dup
14
+ @option = other.option.dup
15
+ end
16
+ private def temp_opt(**kw)
17
+ if kw.empty?
18
+ @option
19
+ else
20
+ ret = @option.dup
21
+ kw.each do |key, value|
22
+ ret.method((key.to_s+'=').to_sym).call(value)
23
+ ret.update_sa_min(@recipe) if key == :target_weight
24
+ ret.irep.add_reinforcement(value) if key == :reinforcement || key == :buff
25
+ if key == :left_associative
26
+ ret.irep = @recipe.ir(opt: ret).add_reinforcement(ret.reinforcement)
27
+ end
28
+ end
29
+ ret
30
+ end
31
+ end
32
+ def option(**kw)
33
+ @option = temp_opt(**kw)
34
+ @option
35
+ end
36
+ def option=(new_option)
37
+ @option = new_option.set_default(@recipe)
38
+ end
39
+ def replace(new_recipe, para: @para, **kw)
40
+ @recipe = new_recipe
41
+ @recipe.each(&:freeze) if @recipe.kind_of?(Enumerable)
42
+ @recipe.freeze
43
+ @para = para
44
+ @option = Option.new(**kw).set_default(@recipe)
45
+ self
46
+ end
47
+ def min_weight
48
+ @recipe.min_weight(opt: @option)
49
+ end
50
+ def max_weight(include_outsourcing=false)
51
+ @recipe.max_weight(opt: @option)
52
+ end
53
+ def min_level(w=@option.target_weight, include_outsourcing=false)
54
+ @recipe.min_level(w, include_outsourcing, opt: @option)
55
+ end
56
+ def min_levels(w=1)
57
+ @recipe.min_levels(w, opt: @option)
58
+ end
59
+ def min_levels_max(w=1)
60
+ @recipe.min_levels_max(w, opt: @option)
61
+ end
62
+ def min_smith
63
+ @recipe.min_smith
64
+ end
65
+ def min_comp
66
+ @recipe.min_comp
67
+ end
68
+ def build(smith=-1, armor=smith, comp=armor.tap{armor=smith}, **kw)
69
+ opt = temp_opt(**kw)
70
+ smith, armor, comp = opt.smith_min, opt.armor_min, opt.comp_min if smith.nil?
71
+ case @recipe
72
+ when String
73
+ recipe.build(smith, comp, opt:)
74
+ when Enumerable
75
+ recipe.build(smith, armor, comp, opt:)
76
+ else
77
+ raise BrokenRecipeError
78
+ end
79
+ end
80
+ def show(smith=-1, armor=smith, comp=armor.tap{armor=smith}, para: @para, **kw)
81
+ opt = temp_opt(**kw)
82
+ smith, armor, comp = opt.smith_min, opt.armor_min, opt.comp_min if smith.nil?
83
+ case @recipe
84
+ when String
85
+ recipe.show(smith, comp, para:, opt:)
86
+ when Enumerable
87
+ recipe.show(smith, armor, comp, para:, opt:)
88
+ else
89
+ raise BrokenRecipeError
90
+ end
91
+ end
92
+ def search(target, para: @para, **kw)
93
+ opt = temp_opt(**kw)
94
+ @recipe.search(para, target, opt:)
95
+ end
96
+ private def correct_level(s, ac, x, opt)
97
+ if s.nil?
98
+ if x.equal?(false)
99
+ s, ac, x = opt.smith_min, opt.comp_min, nil
100
+ else
101
+ s, ac, x = opt.smith_min, opt.armor_min, opt.comp_min
102
+ end
103
+ else
104
+ s = 0 if s < 0
105
+ ac = 0 if ac < 0
106
+ if x.equal?(false)
107
+ x = nil
108
+ else
109
+ x = 0 if x < 0
110
+ end
111
+ end
112
+ [s, ac, x]
113
+ end
114
+ def para_call(smith=-1, armor=smith, comp=armor.tap{armor=smith}, para: @para, **kw)
115
+ opt = temp_opt(**kw)
116
+ smith, armor, comp = correct_level(smith, armor, comp, opt)
117
+ case @recipe
118
+ when String
119
+ opt.irep.para_call(para, smith, comp)
120
+ when Enumerable
121
+ opt.irep.para_call(para, smith, armor, comp)
122
+ else
123
+ raise InvalidRecipeError
124
+ end
125
+ end
126
+ def ir(**kw)
127
+ temp_opt(**kw).irep
128
+ end
129
+ %i|attack phydef magdef hp mp str dex speed magic atkstr atk_sd dex_as mag_das magic2 magmag pmdef|.each do |sym|
130
+ define_method(sym) do |s, ac=s, x=false, **kw|
131
+ s, ac, x = correct_level(s, ac, x, temp_opt(**kw))
132
+ ir(**kw).method(sym).call(s, ac, x)
133
+ end
134
+ end
135
+ %i|power fpower|.each do |sym|
136
+ define_method(sym) do |s, a=s, c=a.tap{a=s}, **kw|
137
+ s, a, c = correct_level(s, a, c, temp_opt(**kw))
138
+ ir(**kw).method(sym).call(s, a, c)
139
+ end
140
+ end
141
+ %i|smith_cost comp_cost cost|.each do |sym|
142
+ define_method(sym) do |s, c=s, outsourcing=false, **kw|
143
+ s, c, x = correct_level(s, c, false, temp_opt(**kw))
144
+ ir(**kw).method(sym).call(s, c, out_sourcing)
145
+ end
146
+ end
147
+ def poly(para=@para, **kw)
148
+ opt = temp_opt(**kw)
149
+ if @recipe.kind_of?(String)
150
+ @recipe.poly(para, opt:)
151
+ else
152
+ raise InvalidRecipeError, "Mgmg::Recipe#poly is available only for String recipes."
153
+ end
154
+ end
155
+ def phydef_optimize(smith=nil, comp=smith, **kw)
156
+ opt = temp_opt(**kw)
157
+ if @recipe.kind_of?(String)
158
+ @recipe.phydef_optimize(smith, comp, opt:)
159
+ else
160
+ raise InvalidRecipeError, "Mgmg::Recipe#phydef_optimize is available only for String recipes."
161
+ end
162
+ end
163
+ def buster_optimize(smith=nil, comp=smith, **kw)
164
+ opt = temp_opt(**kw)
165
+ if @recipe.kind_of?(String)
166
+ @recipe.buster_optimize(smith, comp, opt:)
167
+ else
168
+ raise InvalidRecipeError, "Mgmg::Recipe#buster_optimize is available only for String recipes."
169
+ end
170
+ end
171
+ end
172
+ end
data/lib/mgmg/search.rb CHANGED
@@ -48,11 +48,11 @@ class String
48
48
  def search(para, target, opt: Mgmg::Option.new)
49
49
  opt = opt.dup.set_default(self)
50
50
  opt_nocut = opt.dup; opt_nocut.cut_exp = Float::INFINITY
51
- opt.comp_min = comp_search(para, target, opt.smith_max, opt: opt)
51
+ opt.comp_min = comp_search(para, target, opt.smith_max, opt:)
52
52
  opt.smith_max = smith_search(para, target, opt.comp_min, opt: opt_nocut)
53
53
  opt.smith_min = smith_search(para, target, opt.comp_max, opt: opt_nocut)
54
54
  raise Mgmg::SearchCutException if opt.cut_exp < Mgmg.exp(opt.smith_min, opt.comp_min)
55
- opt.comp_max = comp_search(para, target, opt.smith_min, opt: opt)
55
+ opt.comp_max = comp_search(para, target, opt.smith_min, opt:)
56
56
  ret = nil
57
57
  exp = Mgmg.exp(opt.smith_min, opt.comp_max)
58
58
  opt.cut_exp, ret = exp, [opt.smith_min, opt.comp_max] if exp < opt.cut_exp
@@ -60,7 +60,7 @@ class String
60
60
  opt.cut_exp, ret = exp, [opt.smith_max, opt.comp_min] if exp < opt.cut_exp
61
61
  (opt.comp_min+opt.step).step(opt.comp_max-1, opt.step) do |comp|
62
62
  break if opt.cut_exp < Mgmg.exp(opt.smith_min, comp)
63
- smith = smith_search(para, target, comp, opt: opt)
63
+ smith = smith_search(para, target, comp, opt:)
64
64
  exp = Mgmg.exp(smith, comp)
65
65
  if exp < opt.cut_exp
66
66
  opt.cut_exp, ret = exp, [smith, comp]
@@ -144,7 +144,7 @@ module Enumerable
144
144
  opt.cut_exp, ret = exp2, [opt.smith_max, opt.armor_min] if exp2 < opt.cut_exp
145
145
  (opt.armor_min+1).upto(opt.armor_max-1) do |armor|
146
146
  break if opt.cut_exp < Mgmg.exp(opt.smith_min, armor, comp)
147
- smith = smith_search(para, target, armor, comp, opt: opt)
147
+ smith = smith_search(para, target, armor, comp, opt:)
148
148
  exp = Mgmg.exp(smith, armor, comp)
149
149
  if exp < opt.cut_exp
150
150
  opt.cut_exp, ret = exp, [smith, armor]
@@ -158,7 +158,7 @@ module Enumerable
158
158
  else
159
159
  (opt.smith_min+1).upto(opt.smith_max-1) do |smith|
160
160
  break if opt.cut_exp < Mgmg.exp(smith, opt.armor_min, comp)
161
- armor = armor_search(para, target, smith, comp, opt: opt)
161
+ armor = armor_search(para, target, smith, comp, opt:)
162
162
  exp = Mgmg.exp(smith, armor, comp)
163
163
  if exp < opt.cut_exp
164
164
  opt.cut_exp, ret = exp, [smith, armor]
@@ -195,11 +195,11 @@ module Enumerable
195
195
  end
196
196
  def search(para, target, opt: Mgmg::Option.new)
197
197
  opt = opt.dup.set_default(self)
198
- opt.comp_min = comp_search(para, target, opt.smith_max, opt.armor_max, opt: opt)
199
- opt.smith_max, opt.armor_max = sa_search(para, target, opt.comp_min, opt: opt)
200
- opt.smith_min, opt.armor_min = sa_search(para, target, opt.comp_max, opt: opt)
198
+ opt.comp_min = comp_search(para, target, opt.smith_max, opt.armor_max, opt:)
199
+ opt.smith_max, opt.armor_max = sa_search(para, target, opt.comp_min, opt:)
200
+ opt.smith_min, opt.armor_min = sa_search(para, target, opt.comp_max, opt:)
201
201
  raise Mgmg::SearchCutException if opt.cut_exp < Mgmg.exp(opt.smith_min, opt.armor_min, opt.comp_min)
202
- opt.comp_max = comp_search(para, target, opt.smith_min, opt.armor_min, opt: opt)
202
+ opt.comp_max = comp_search(para, target, opt.smith_min, opt.armor_min, opt:)
203
203
  ret = nil
204
204
  exp = Mgmg.exp(opt.smith_min, opt.armor_min, opt.comp_max)
205
205
  opt.cut_exp, ret = exp, [opt.smith_min, opt.armor_min,opt. comp_max] if exp < opt.cut_exp
@@ -207,7 +207,7 @@ module Enumerable
207
207
  opt.cut_exp, ret = exp, [opt.smith_max, opt.armor_max, opt.comp_min] if exp < opt.cut_exp
208
208
  (opt.comp_min+1).upto(opt.comp_max-1) do |comp|
209
209
  break if opt.cut_exp < Mgmg.exp(opt.smith_min, opt.armor_min, comp)
210
- smith, armor = sa_search(para, target, comp, opt: opt)
210
+ smith, armor = sa_search(para, target, comp, opt:)
211
211
  exp = Mgmg.exp(smith, armor, comp)
212
212
  if exp < opt.cut_exp
213
213
  opt.cut_exp, ret = exp, [smith, armor, comp]
@@ -228,7 +228,18 @@ module Mgmg
228
228
  if term <= start
229
229
  raise ArgumentError, "start < term is needed, (start, term) = (#{start}, #{term}) are given"
230
230
  end
231
- opt_a, opt_b = opt_a.dup.set_default(a), opt_b.dup.set_default(b)
231
+ if a.kind_of?(Recipe)
232
+ opt_a = a.option.dup
233
+ a = a.recipe
234
+ else
235
+ opt_a = opt_a.dup.set_default(a)
236
+ end
237
+ if b.kind_of?(Recipe)
238
+ opt_b = b.option.dup
239
+ b = b.recipe
240
+ else
241
+ opt_b = opt_b.dup.set_default(b)
242
+ end
232
243
  sca, scb = a.search(para, start, opt: opt_a), b.search(para, start, opt: opt_b)
233
244
  ea, eb = Mgmg.exp(*sca), Mgmg.exp(*scb)
234
245
  if eb < ea || ( ea == eb && opt_a.irep.para_call(para, *sca) < opt_b.irep.para_call(para, *scb) )
@@ -263,7 +274,18 @@ module Mgmg
263
274
  if start <= term
264
275
  raise ArgumentError, "term < start is needed, (start, term) = (#{start}, #{term}) are given"
265
276
  end
266
- opt_a, opt_b = opt_a.dup.set_default(a), opt_b.dup.set_default(b)
277
+ if a.kind_of?(Recipe)
278
+ opt_a = a.option.dup
279
+ a = a.recipe
280
+ else
281
+ opt_a = opt_a.dup.set_default(a)
282
+ end
283
+ if b.kind_of?(Recipe)
284
+ opt_b = b.option.dup
285
+ b = b.recipe
286
+ else
287
+ opt_b = opt_b.dup.set_default(b)
288
+ end
267
289
  sca, scb = a.search(para, start, opt: opt_a), b.search(para, start, opt: opt_b)
268
290
  ea, eb = Mgmg.exp(*sca), Mgmg.exp(*scb)
269
291
  if ea < eb || ( ea == eb && opt_b.irep.para_call(para, *scb) < opt_a.irep.para_call(para, *sca) )
@@ -323,10 +323,8 @@ module Mgmg
323
323
  SystemEquip.store(k.sub(/脛当て\Z/, 'すね当て'), SystemEquip[k])
324
324
  end
325
325
  end
326
- SystemEquip.freeze
327
326
  SystemEquipRegexp = Hash.new
328
- SystemEquip.keys.each do |k|
329
- SystemEquipRegexp.store(k.freeze, Regexp.compile(k))
327
+ SystemEquip.each_key do |k|
328
+ SystemEquipRegexp.store(k, Regexp.compile(k).freeze)
330
329
  end
331
- SystemEquipRegexp.freeze
332
330
  end
data/lib/mgmg/utils.rb CHANGED
@@ -17,16 +17,33 @@ module Mgmg
17
17
  refine Float do
18
18
  alias :cdiv :quo # Floatの場合は普通の割り算
19
19
  def comma3
20
- s = self.to_s
21
- case s
22
- when %r|e|
23
- s
24
- when %r|\.|
25
- ary = s.split('.')
26
- ary[0].gsub(/(\d)(?=(\d{3})+(?!\d))/, '\1,') + '.' + ary[1]
20
+ s = (self*100).round.to_s
21
+ if s[0] == '-'
22
+ g, s = '-', s[1..(-1)]
27
23
  else
28
- s
24
+ g = ''
29
25
  end
26
+ raise unless %r|\A\d+\Z|.match(s)
27
+ case s.length
28
+ when 1
29
+ if s == '0'
30
+ '0.0'
31
+ else
32
+ g+'0.0'+s
33
+ end
34
+ when 2
35
+ if s[1] == '0'
36
+ g+'0.'+s[0]
37
+ else
38
+ g+'0.'+s
39
+ end
40
+ else
41
+ i, d = s[0..(-3)], s[(-2)..(-1)]
42
+ d = d[0] if d[1] == '0'
43
+ g+i.gsub(/(\d)(?=(\d{3})+(?!\d))/, '\1,') + '.' + d
44
+ end
45
+ rescue
46
+ self.to_s
30
47
  end
31
48
  end
32
49
  refine Rational do
@@ -78,6 +95,20 @@ module Mgmg
78
95
  end
79
96
  attr_accessor :name
80
97
  end
98
+ class InvalidRecipeError < StandardError
99
+ def initialize(msg=nil)
100
+ if msg.nil?
101
+ super("Neither String nor Enumerable recipe was set.")
102
+ else
103
+ super(msg)
104
+ end
105
+ end
106
+ end
107
+ class Over20Error < StandardError
108
+ def initialize(star)
109
+ super("The star of given recipe is #{star}. It can't be built since the star is over 20.")
110
+ end
111
+ end
81
112
  class SearchCutException < StandardError; end
82
113
  class UnexpectedError < StandardError
83
114
  def initialize()
@@ -122,6 +153,11 @@ module Mgmg
122
153
  module_function def invexp3(exp, sa, comp)
123
154
  Math.sqrt(exp - ((sa-1)**2) - (2*((comp-1)**2)) - 4).round + 1
124
155
  end
156
+ module_function def option(recipe=nil, **kw)
157
+ ret = Option.new(**kw)
158
+ ret.set_default(recipe) unless recipe.nil?
159
+ ret
160
+ end
125
161
 
126
162
  CharacterList = /[^\(\)\+0123456789\[\]あきくしすたてなねのびりるイウガクグサジスタダチツデトドニノフブペボムラリルロンヴー一万二光兜典刀剣劣匠双古名吹咆品哮地大天太子安宝小帽弓弩当息悪戦手指斧書服木本杖業樹歴殺水氷法火炎牙物玉王産用界異的皮盾短石砕竜紫綿耳聖脛腕腿般良色衣袋覇質軍軽輝輪重量金鉄鎧闇陽靴額飾首骨鬼龍]/.freeze
127
163
  module_function def check_string(str)
data/lib/mgmg/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Mgmg
2
- VERSION = "1.5.0"
2
+ VERSION = "1.5.3"
3
3
  end
data/lib/mgmg.rb CHANGED
@@ -8,26 +8,33 @@ require_relative './mgmg/system_equip'
8
8
  require_relative './mgmg/cuisine'
9
9
  require_relative './mgmg/reinforce'
10
10
  require_relative './mgmg/option'
11
+ require_relative './mgmg/recipe'
11
12
  require_relative './mgmg/search'
12
13
  require_relative './mgmg/optimize'
13
14
 
14
15
  class String
16
+ using Mgmg::Refiner
17
+ def to_recipe(para=:power, allow_over20: false, **kw)
18
+ ret = Mgmg::Recipe.new(self, para, **kw)
19
+ raise Mgmg::Over20Error, ret.ir.star if (!allow_over20 and 20<ret.ir.star)
20
+ ret
21
+ end
15
22
  def min_weight(opt: Mgmg::Option.new)
16
- build(build(opt: opt).min_levels_max, opt: opt).weight
23
+ build(build(opt:).min_levels_max, opt:).weight
17
24
  end
18
25
  def max_weight(include_outsourcing=false, opt: Mgmg::Option.new)
19
26
  if include_outsourcing
20
- build(-1, opt: opt).weight
27
+ build(-1, opt:).weight
21
28
  else
22
- build(min_smith(opt: opt), opt: opt).weight
29
+ build(min_smith(opt:), opt:).weight
23
30
  end
24
31
  end
25
32
  def min_level(w=0, include_outsourcing=false, opt: Mgmg::Option.new)
26
- built = build(-1, opt: opt)
27
- w = build(built.min_levels_max, -1, opt: opt).weight - w if w <= 0
33
+ built = build(-1, opt:)
34
+ w = build(built.min_levels_max, -1, opt:).weight - w if w <= 0
28
35
  return -1 if include_outsourcing && built.weight <= w
29
- ms = min_smith(opt: opt)
30
- return ms if build(ms, opt: opt).weight <= w
36
+ ms = min_smith(opt:)
37
+ return ms if build(ms, opt:).weight <= w
31
38
  ary = [ms]
32
39
  4.downto(1) do |wi| # 単品の最大重量は[斧|重鎧](金10石10)の5
33
40
  built.min_levels(wi).values.each do |v|
@@ -35,21 +42,21 @@ class String
35
42
  end
36
43
  end
37
44
  ary.sort.each do |l|
38
- return l if build(l, opt: opt).weight <= w
45
+ return l if build(l, opt:).weight <= w
39
46
  end
40
- raise ArgumentError, "w=`#{w}' is given, but the minimum weight for the recipe is `#{min_weight(opt: opt)}'."
47
+ raise ArgumentError, "w=`#{w}' is given, but the minimum weight for the recipe is `#{min_weight(opt:)}'."
41
48
  end
42
49
  def min_levels(w=1, opt: Mgmg::Option.new)
43
- build(opt: opt).min_levels(w)
50
+ build(opt:).min_levels(w)
44
51
  end
45
52
  def min_levels_max(w=1, opt: Mgmg::Option.new)
46
- min_levels(w, opt: opt).values.append(-1).max
53
+ min_levels(w, opt:).values.append(-1).max
47
54
  end
48
55
  def min_smith(opt: Mgmg::Option.new)
49
- Mgmg::Equip.min_smith(self, opt: opt)
56
+ Mgmg::Equip.min_smith(self, opt:)
50
57
  end
51
58
  def min_comp(opt: Mgmg::Option.new)
52
- Mgmg::Equip.min_comp(self, opt: opt)
59
+ Mgmg::Equip.min_comp(self, opt:)
53
60
  end
54
61
  def build(smith=-1, comp=smith, opt: Mgmg::Option.new)
55
62
  Mgmg::Equip.build(self, smith, comp, left_associative: opt.left_associative).reinforce(*opt.reinforcement)
@@ -60,28 +67,30 @@ class String
60
67
  def poly(para=:cost, opt: Mgmg::Option.new)
61
68
  case para
62
69
  when :atkstr
63
- self.poly(:attack, opt: opt) + self.poly(:str, opt: opt)
70
+ self.poly(:attack, opt:) + self.poly(:str, opt:)
64
71
  when :atk_sd
65
- self.poly(:attack, opt: opt) + self.poly(:str, opt: opt).quo(2) + self.poly(:dex, opt: opt).quo(2)
72
+ self.poly(:attack, opt:) + self.poly(:str, opt:).quo(2) + self.poly(:dex, opt:).quo(2)
66
73
  when :dex_as
67
- self.poly(:dex, opt: opt) + self.poly(:attack, opt: opt).quo(2) + self.poly(:str, opt: opt).quo(2)
74
+ self.poly(:dex, opt:) + self.poly(:attack, opt:).quo(2) + self.poly(:str, opt:).quo(2)
68
75
  when :mag_das
69
- self.poly(:magic, opt: opt) + self.poly(:dex_as, opt: opt).quo(2)
76
+ self.poly(:magic, opt:) + self.poly(:dex_as, opt:).quo(2)
77
+ when :magic2
78
+ self.poly(:magic, opt:).scalar(2)
70
79
  when :magmag
71
- self.poly(:magdef, opt: opt) + self.poly(:magic, opt: opt).quo(2)
80
+ self.poly(:magdef, opt:) + self.poly(:magic, opt:).quo(2)
72
81
  when :pmdef
73
- pd = self.poly(:phydef, opt: opt)
74
- md = self.poly(:magmag, opt: opt)
82
+ pd = self.poly(:phydef, opt:)
83
+ md = self.poly(:magmag, opt:)
75
84
  pd <= md ? pd : md
76
85
  when :cost
77
- if Mgmg::SystemEquip.keys.include?(self)
86
+ if Mgmg::SystemEquip.has_key?(self)
78
87
  return Mgmg::TPolynomial.new(Mgmg::Mat.new(1, 1, 0.quo(1)), 28, 0, 12, 12)
79
88
  end
80
- built = self.build(-1, opt: opt)
89
+ built = self.build(-1, opt:)
81
90
  const = (built.star**2) * ( /\+/.match(self) ? 5 : ( built.kind < 8 ? 2 : 1 ) )
82
- ret = poly(:attack, opt: opt) + poly(:phydef, opt: opt) + poly(:magdef, opt: opt)
83
- ret += poly(:hp, opt: opt).quo(4) + poly(:mp, opt: opt).quo(4)
84
- ret += poly(:str, opt: opt) + poly(:dex, opt: opt) + poly(:speed, opt: opt) + poly(:magic, opt: opt)
91
+ ret = poly(:attack, opt:) + poly(:phydef, opt:) + poly(:magdef, opt:)
92
+ ret += poly(:hp, opt:).quo(4) + poly(:mp, opt:).quo(4)
93
+ ret += poly(:str, opt:) + poly(:dex, opt:) + poly(:speed, opt:) + poly(:magic, opt:)
85
94
  ret.mat.body[0][0] += const
86
95
  ret
87
96
  else
@@ -89,15 +98,15 @@ class String
89
98
  end
90
99
  end
91
100
  def eff(para, smith, comp=smith, opt: Mgmg::Option.new)
92
- a = build(smith, comp, opt: opt).para_call(para)
93
- b = build(smith+1, comp, opt: opt).para_call(para)
94
- c = build(smith, comp+2, opt: opt).para_call(para)
101
+ a = build(smith, comp, opt:).para_call(para)
102
+ b = build(smith+1, comp, opt:).para_call(para)
103
+ c = build(smith, comp+2, opt:).para_call(para)
95
104
  sden = smith==0 ? 1 : 2*smith-1
96
105
  cden = comp==0 ? 4 : 8*comp
97
106
  [(b-a).quo(sden), (c-a).quo(cden)]
98
107
  end
99
108
  def peff(para, smith, comp=smith, opt: Mgmg::Option.new)
100
- poly(para, opt: opt).eff(smith, comp)
109
+ poly(para, opt:).eff(smith, comp)
101
110
  end
102
111
  def show(smith=-1, comp=smith, para: :power, opt: Mgmg::Option.new)
103
112
  rein = case opt.reinforcement
@@ -106,38 +115,42 @@ class String
106
115
  else
107
116
  [Mgmg::Reinforcement.compile(opt.reinforcement)]
108
117
  end
109
- built = build(smith, comp, opt: opt)
118
+ built = build(smith, comp, opt:)
110
119
  pstr = '%.3f' % built.para_call(para)
111
120
  pstr.sub!(/\.?0+\Z/, '')
112
- puts "Building"
121
+ puts "With levels (#{smith}, #{comp}: #{Mgmg.exp(smith, comp).comma3}), building"
113
122
  puts " #{self}"
114
- rein = rein.empty? ? '' : " reinforced by {#{rein.join(',')}}"
115
- puts "with levels (#{smith}, #{comp})#{rein} yields (#{pstr}, #{built.total_cost})"
123
+ rein = rein.empty? ? '' : "reinforced by {#{rein.join(',')}} "
124
+ puts "#{rein}yields (#{pstr}, #{built.total_cost})"
116
125
  puts " #{built}"
117
126
  end
118
127
  def phydef_optimize(smith=nil, comp=smith, opt: Mgmg::Option.new)
119
- Mgmg::Optimize.phydef_optimize(self, smith, comp, opt: opt)
128
+ Mgmg::Optimize.phydef_optimize(self, smith, comp, opt:)
120
129
  end
121
130
  def buster_optimize(smith=nil, comp=smith, opt: Mgmg::Option.new)
122
- Mgmg::Optimize.buster_optimize(self, smith, comp, opt: opt)
131
+ Mgmg::Optimize.buster_optimize(self, smith, comp, opt:)
123
132
  end
124
133
  end
125
134
  module Enumerable
135
+ using Mgmg::Refiner
136
+ def to_recipe(para=:power, **kw)
137
+ Mgmg::Recipe.new(self, para, **kw)
138
+ end
126
139
  def build(smith=-1, armor=smith, comp=armor.tap{armor=smith}, opt: Mgmg::Option.new)
127
140
  opt = opt.dup
128
141
  rein = opt.reinforcement
129
142
  opt.reinforcement = []
130
143
  self.sum(Mgmg::Equip::Zero) do |str|
131
- if Mgmg::EquipPosition[str.build(opt: opt).kind] == 0
132
- str.build(smith, comp, opt: opt)
144
+ if Mgmg::EquipPosition[str.build(opt:).kind] == 0
145
+ str.build(smith, comp, opt:)
133
146
  else
134
- str.build(armor, comp, opt: opt)
147
+ str.build(armor, comp, opt:)
135
148
  end
136
149
  end.reinforce(*rein)
137
150
  end
138
151
  def ir(opt: Mgmg::Option.new)
139
152
  self.sum(Mgmg::IR::Zero) do |str|
140
- str.ir(opt: opt)
153
+ str.ir(opt:)
141
154
  end.add_reinforcement(opt.reinforcement)
142
155
  end
143
156
  def show(smith=-1, armor=smith, comp=armor.tap{armor=smith}, para: :power, opt: Mgmg::Option.new)
@@ -147,66 +160,66 @@ module Enumerable
147
160
  else
148
161
  [Mgmg::Reinforcement.compile(opt.reinforcement)]
149
162
  end
150
- built = self.build(smith, armor, comp, opt: opt)
163
+ built = self.build(smith, armor, comp, opt:)
151
164
  pstr = '%.3f' % built.para_call(para)
152
165
  pstr.sub!(/\.?0+\Z/, '')
153
- puts "Building"
166
+ puts "With levels (#{smith}, #{armor}, #{comp}: #{Mgmg.exp(smith, armor, comp).comma3}), building"
154
167
  puts " #{self.join(', ')}"
155
- rein = rein.empty? ? '' : " reinforced by {#{rein.join(',')}}"
156
- puts "with levels (#{smith}, #{armor}, #{comp})#{rein} yields (#{pstr}, #{built.total_cost})"
168
+ rein = rein.empty? ? '' : "reinforced by {#{rein.join(',')}} "
169
+ puts "#{rein}yields (#{pstr}, #{built.total_cost})"
157
170
  puts " #{built}"
158
171
  end
159
172
  def min_weight(opt: Mgmg::Option.new)
160
- build(*build(opt: opt).min_levels_max, -1, opt: opt).weight
173
+ build(*build(opt:).min_levels_max, -1, opt:).weight
161
174
  end
162
175
  def max_weight(include_outsourcing=false, opt: Mgmg::Option.new)
163
176
  if include_outsourcing
164
- build(-1, opt: opt).weight
177
+ build(-1, opt:).weight
165
178
  else
166
- build(*min_smith(opt: opt), -1, opt: opt).weight
179
+ build(*min_smith(opt:), -1, opt:).weight
167
180
  end
168
181
  end
169
182
  def min_weights(opt: Mgmg::Option.new)
170
183
  weapons, armors = [], []
171
184
  each do |str|
172
- if Mgmg::EquipPosition[str.build(opt: opt).kind] == 0
185
+ if Mgmg::EquipPosition[str.build(opt:).kind] == 0
173
186
  weapons << str
174
187
  else
175
188
  armors << str
176
189
  end
177
190
  end
178
- [weapons.min_weight(opt: opt), armors.min_weight(opt: opt)]
191
+ [weapons.min_weight(opt:), armors.min_weight(opt:)]
179
192
  end
180
193
  def max_weights(include_outsourcing=false, opt: Mgmg::Option.new)
181
194
  weapons, armors = [], []
182
195
  each do |str|
183
- if Mgmg::EquipPosition[str.build(opt: opt).kind] == 0
196
+ if Mgmg::EquipPosition[str.build(opt:).kind] == 0
184
197
  weapons << str
185
198
  else
186
199
  armors << str
187
200
  end
188
201
  end
189
- [weapons.max_weight(include_outsourcing, opt: opt), armors.max_weight(include_outsourcing, opt: opt)]
202
+ [weapons.max_weight(include_outsourcing, opt:), armors.max_weight(include_outsourcing, opt:)]
190
203
  end
191
204
  def min_level(ws=0, wa=ws, include_outsourcing=false, opt: Mgmg::Option.new)
192
205
  weapons, armors = [], []
193
206
  each do |str|
194
- if Mgmg::EquipPosition[str.build(opt: opt).kind] == 0
207
+ if Mgmg::EquipPosition[str.build(opt:).kind] == 0
195
208
  weapons << str
196
209
  else
197
210
  armors << str
198
211
  end
199
212
  end
200
- ms, ma = min_smith(opt: opt)
201
- rs = min_level_sub(ws, ms, 0, weapons, include_outsourcing, opt: opt)
202
- ra = min_level_sub(wa, ma, 1, armors, include_outsourcing, opt: opt)
213
+ ms, ma = min_smith(opt:)
214
+ rs = min_level_sub(ws, ms, 0, weapons, include_outsourcing, opt:)
215
+ ra = min_level_sub(wa, ma, 1, armors, include_outsourcing, opt:)
203
216
  [rs, ra]
204
217
  end
205
218
  private def min_level_sub(w, ms, i, recipe, include_outsourcing, opt: Mgmg::Option.new)
206
- built = recipe.build(opt: opt)
207
- w = recipe.build(built.min_levels_max[i], opt: opt).weight - w if w <= 0
219
+ built = recipe.build(opt:)
220
+ w = recipe.build(built.min_levels_max[i], opt:).weight - w if w <= 0
208
221
  return -1 if include_outsourcing && built.weight <= w
209
- return ms if build(ms, opt: opt).weight <= w
222
+ return ms if build(ms, opt:).weight <= w
210
223
  ary = [ms]
211
224
  4.downto(1) do |wi|
212
225
  built.min_levels(wi).values.each do |v|
@@ -214,17 +227,17 @@ module Enumerable
214
227
  end
215
228
  end
216
229
  ary.sort.each do |l|
217
- return l if recipe.build(l, opt: opt).weight <= w
230
+ return l if recipe.build(l, opt:).weight <= w
218
231
  end
219
- raise ArgumentError, "w#{%w|s a|[i]}=`#{w}' is given, but the minimum weight for the #{%w|weapon(s) armor(s)|[i]} is `#{recipe.min_weight(opt: opt)}'."
232
+ raise ArgumentError, "w#{%w|s a|[i]}=`#{w}' is given, but the minimum weight for the #{%w|weapon(s) armor(s)|[i]} is `#{recipe.min_weight(opt:)}'."
220
233
  end
221
234
  def min_levels(w=1, opt: Mgmg::Option.new)
222
- build(opt: opt).min_levels(w)
235
+ build(opt:).min_levels(w)
223
236
  end
224
237
  def min_levels_max(w=1, opt: Mgmg::Option.new)
225
238
  ret = [-1, -1]
226
- min_levels(w, opt: opt).each do |str, level|
227
- if Mgmg::EquipPosition[str.build(opt: opt).kind] == 0
239
+ min_levels(w, opt:).each do |str, level|
240
+ if Mgmg::EquipPosition[str.build(opt:).kind] == 0
228
241
  ret[0] = [ret[0], level].max
229
242
  else
230
243
  ret[1] = [ret[1], level].max
@@ -235,8 +248,8 @@ module Enumerable
235
248
  def min_smith(opt: Mgmg::Option.new)
236
249
  ret = [-1, -1]
237
250
  self.each do |str|
238
- s = Mgmg::Equip.min_smith(str, opt: opt)
239
- if Mgmg::EquipPosition[str.build(opt: opt).kind] == 0
251
+ s = Mgmg::Equip.min_smith(str, opt:)
252
+ if Mgmg::EquipPosition[str.build(opt:).kind] == 0
240
253
  ret[0] = [ret[0], s].max
241
254
  else
242
255
  ret[1] = [ret[1], s].max
@@ -246,7 +259,7 @@ module Enumerable
246
259
  end
247
260
  def min_comp(opt: Mgmg::Option.new)
248
261
  self.map do |str|
249
- Mgmg::Equip.min_comp(str, opt: opt)
262
+ Mgmg::Equip.min_comp(str, opt:)
250
263
  end.append(-1).max
251
264
  end
252
265
  end
data/reference.md CHANGED
@@ -1,6 +1,11 @@
1
1
  # リファレンス
2
2
  本ライブラリで定義される主要なメソッドを以下に解説します.
3
3
 
4
+ ## `String#to_recipe(para=:power, allow_over20: false, **kw)`,`Enumerable#to_recipe(para=:power, **kw)`
5
+ レシピ文字列である`self`と,注目パラメータ`para`,オプション`Mgmg.option(**kw)`をセットにした[後述](#mgmgrecipe)の`Mgmg::Recipe`オブジェクトを生成して返します.デフォルト値としてセットされたパラメータを使用する以外は,レシピ文字列と同様に扱えます.
6
+
7
+ `allow_over20`が偽の場合,レシピの☆を確認し,20を超える場合は,例外`Mgmg::Over20Error`を発生します.このチェックを抑制したい場合は,真にしてください.
8
+
4
9
  ## `String#build(smith=-1, comp=smith, opt: Mgmg.option())`
5
10
  レシピ文字列である`self`を解釈し,鍛冶・防具製作Lvを`smith`,道具製作Lvを`comp`として鍛冶・防具製作及び武器・防具合成を行った結果を[後述](#mgmgequip)の`Mgmg::Equip`クラスのインスタンスとして生成し,返します.例えば,
6
11
  ```ruby
@@ -145,7 +150,8 @@
145
150
  返り値は`[逆転しない最大目標値, 逆転時の最小para値]`です.前者は逆転目標値の下限,後者は,目標値が前者よりも少しだけ大きいときの`para`値です.
146
151
  ここで,最小経験値が小さい方,または最小経験値が同じなら,そのときの`para`値が大きい方をよりよいものと解釈します.
147
152
  `term`は`start`より大きい値とします.目標値`term`における優劣が,目標値`start`における優劣と同じ場合,`Mgmg::SearchCutException`を発生します.
148
- `a`と`b`は`String`でもその`Enumerable`でも構いません.
153
+
154
+ `a`と`b`は`String`でもその`Enumerable`でも構いません.`Mgmg::Recipe`が与えられた場合,`opt_a`,`opt_b`は無視され,代わりに`a.option`,`b.option`が使用されます.`a.para`,`b.para`は無視され,引数で与えた`para`を使用します.
149
155
 
150
156
  `opt_a`,`opt_b`には,それぞれ`a`と`b`に関するオプションパラメータを指定します.`smith_min`,`armor_min`,`min_smith`,`left_associative`,`reinforcement`,`irep`を使用します.
151
157
 
@@ -451,3 +457,55 @@ alias として`*`があるほか`scalar(1.quo(value))`として`quo`,`/`,`s
451
457
  |irep|`recipe.ir()`|`Mgmg::IR`の使い回しによる高速化|`String#search`など,内部的に使用|
452
458
  |cut_exp|`Float::INFINITY`|探索時の総経験値の打ち切り値|`String#search`など,内部的に使用|
453
459
 
460
+ ## `Mgmg::Recipe`
461
+ レシピ文字列,注目パラメータ,オプションをセットにして扱うためのクラスです.
462
+ それぞれ`Mgmg::Recipe#recipe`,`Mgmg::Recipe#para`,`Mgmg::Recipe#option`でアクセスできます.
463
+
464
+ ## `Mgmg::Recipe#option(**kw)`
465
+ オプションパラメータを`kw`で上書きして,新しい`Mgmg::Option`インスタンスを返します.
466
+ 渡したバラメータだけ上書きします.パラメータを渡さない場合,単にセットになってるオプションオブジェクトを返します.
467
+ `target_weight`を上書きした場合,`smith_min`,`armor_min`は自動的に再計算されます.
468
+
469
+ 与えるパラメータ以外をデフォルト値に戻したい場合,`recipe.option=Mgmg.option(**kw)`のように`Mgmg::Recipe#option=(new_option)`を使用します.
470
+
471
+ ## `Mgmg::Recipe#replace(new_recipe, para: self.para, **kw)`
472
+ レシピ文字列を`new_recipe`で置き換え,`kw`でオプションを設定します.`para`を与えた場合,注目パラメータも引数で置き換えます.
473
+ レシピ文字列とオプションの対応を保つため,`Mgmg::Recipe#recipe=`メソッドは定義していません.代わりにこれを使います.
474
+
475
+ ## `Mgmg::Recipe#build(smith=-1, armor=smith, comp=armor.tap{armor=smith}, **kw)`
476
+ `self.recipe.build`を呼び出して返します.`kw`が与えられた場合,オプションのうち,与えられたパラメータのみ一時的に上書きします.
477
+
478
+ `smith`に`nil`を与えた場合,製作Lvを`self.option.smith_min`,`self.option.armor_min`,`self.option.comp_min`とします.
479
+
480
+ ## `Mgmg::Recipe#search(target, para: self.para, **kw)`
481
+ `self.recipe.search`を呼び出して返します.注目パラメータはセットされたものを自動的に渡しますが,別のパラメータを使いたい場合,キーワード引数で指定します.その他のキーワード引数を与えた場合,オプションのうち,与えられたパラメータのみ一時的に上書きします.
482
+
483
+ ## `Mgmg::Recipe#min_level(w=self.option.target_weight, include_outsourcing=false)`
484
+ `self.recipe.min_level(w, include_outsourcing)`を返します.`w`のデフォルトパラメータが`target_weight`になっている以外は`String#min_level`または`Enumerable#min_level`と同じです.
485
+
486
+ ## `Mgmg::Recipe#min_weight, max_weight, min_levels, min_levels_max, min_smith, min_comp`
487
+ `self.recipe`の同名メソッドをそのまま呼び出して返します.
488
+
489
+ ## `Mgmg::Recipe#ir(**kw)`
490
+ `self.recipe`に対応した`Mgmg::IR`インスタンスを返します.
491
+
492
+ `kw`を与えた場合一時的に置き換えたオプションに対応した`Mgmg::IR`インスタンスを返します.`left_associative`が与えられれば再構築し,`reinforcement`が与えられれば,スキル・料理の効果を与えた引数に差し替えます.その他のキーワードを与えても特に影響はありません.
493
+
494
+ ## `Mgmg::Recipe#para_call(smith=-1, armor=smith, comp=armor.tap{armor=smith}, para: self.para, **kw)`
495
+ `self.ir.para_call`を呼び出して返します.キーワード引数が与えられた場合,与えられた分だけ一時的に上書きします.
496
+
497
+ `smith`に`nil`を与えた場合,製作Lvを`self.option.smith_min`,`self.option.armor_min`,`self.option.comp_min`とします.製作Lvに負の値が与えられた場合,`0`に修正して計算して返します.
498
+
499
+ パラメータとして,以下が指定でき,これらは直接メソッドとしても定義されています(`Mgmg::Recipe#attack`など).製作Lvの与え方は`para_call`と同様です.
500
+
501
+ ```ruby
502
+ :attack, :phydef, :magdef, :hp, :mp, :str, :dex, :speed, :magic
503
+ :atkstr, :atk_sd, :dex_as, :mag_das, :magic2, :magmag, :pmdef
504
+ :power, :fpower, :smith_cost, :comp_cost, :cost
505
+ ```
506
+
507
+ ## `Mgmg::Recipe#poly(para=self.para, **kw)`
508
+ `self.recipe.poly(para, opt: self.option)`を返します.`kw`が与えられた場合,オプションのうち,与えられたパラメータのみ一時的に上書きします.
509
+
510
+ ## `Mgmg::Recipe#phydef_optimize(smith=nil, comp=smith, **kw)`,`Mgmg::Recipe#buster_optimize(smith=nil, comp=smith, **kw)`
511
+ `self.recipe.phydef_optimize(smith, comp, opt: self.option)`,`self.recipe.buster_optimize(smith, comp, opt: self.option)`を返します.`kw`が与えられた場合,オプションのうち,与えられたパラメータのみ一時的に上書きします.
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.5.0
4
+ version: 1.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - KAZOON
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-06-22 00:00:00.000000000 Z
11
+ date: 2022-06-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -75,6 +75,7 @@ files:
75
75
  - lib/mgmg/optimize.rb
76
76
  - lib/mgmg/option.rb
77
77
  - lib/mgmg/poly.rb
78
+ - lib/mgmg/recipe.rb
78
79
  - lib/mgmg/reinforce.rb
79
80
  - lib/mgmg/search.rb
80
81
  - lib/mgmg/system_equip.rb