mgmg 1.5.1 → 1.5.4

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: d7cadbd5fef7941e004310c08f8ec597ce06381b0b305369d0e42afa08cb5016
4
- data.tar.gz: a0d9d53d2e0284274e4558c7d27f26aeb2af6eb2649faaa07d840a5cd4f5aed8
3
+ metadata.gz: f3f377aff6ac79ec7111b1b81fcfc5148505ca5f57606be79192f832d80d7c92
4
+ data.tar.gz: 3cca84c8c97b44f18945d1817a921d42b7702f77a61dff4e8836a52f1a9a0f14
5
5
  SHA512:
6
- metadata.gz: fe3d521ee8e90ae3253dacf5389035a53e40d58d0a9e574c0685d346765fdef35de371412d772b77476809cf80a2bb384e1e0b20c0ada8c265f6155ad27fcf3a
7
- data.tar.gz: 77638a9c41f22fa7a509f409645307d2a452822bd557aa83e5d0c8e96ac8e84c93f6d9fd3df265a04dc1e91abde3f143561f410c6b397a89fe599a0ee2dcc8f7
6
+ metadata.gz: 76db175b332d5c5d69ab12f171b734ceb82d4102151b19ce17e710e4776acaabe5e706f0b830eef251a664ae2d702b92d504af6409441da164dd64be1736a19f
7
+ data.tar.gz: aa9416fcbaca9dc1d631925c4b5b3eb946196d81aab90748567c3c638adba12bf6474a70f1c815806527afe334cad434d841ce19e22350816df443e4c485fbdc
data/CHANGELOG.md CHANGED
@@ -138,3 +138,17 @@
138
138
  - `Mgmg::Recipe#build`,`Mgmg::Recipe#search`など`String`等に対する操作と同様にでき,注目パラメータとオプションは,`to_recipe`でセットしたものがデフォルトで使われるようになる.各メソッド呼び出し時にキーワード引数を与えることで,一時的に上書きすることもできる.
139
139
  - `String#to_recipe`にのみ,☆20制限のチェック機構を導入した.
140
140
  - 計算を繰り返した際,複数装備の装備数が増加していってしまうバグを修正した.
141
+
142
+ ## 1.5.2 2022/06/28
143
+ - `String#poly`が`:magic2`に対応していなかったバグを修正.
144
+
145
+ ## 1.5.3 2022/06/28
146
+ - `Recipe#option`がキーワード引数を受け取れなかったバグを修正.
147
+
148
+ ## 1.5.4 2022/06/30
149
+ - 既製品の探索を制御する`:include_system_equips`オプションを追加.
150
+ - 既製品を含まないレシピを計算するとき,これを偽に設定することで,高速化される.
151
+ - デフォルト値は`true`.ただし,既製品を含まないレシピ文字列を`to_recipe`すると,自動的に`false`に書き換える.
152
+ - 一部のメソッドで計算結果をキャッシュすることで,同じ材料装備を含む大量のレシピを計算する場合に高速化された.
153
+ - `Mgmg.#clear_cache`ですべてのキャッシュをクリアできる.
154
+ - 一部のオプションのデフォルト値をグローバルに変更するための定数ハッシュ`Mgmg::Option::Defaults`を追加.
data/lib/mgmg/cuisine.rb CHANGED
@@ -22,6 +22,55 @@ module Mgmg
22
22
  "料理[攻撃:#{self.attack}, 物防:#{self.phydef}, 魔防:#{self.magdef}]"
23
23
  end
24
24
  alias :inspect :to_s
25
+
26
+ MainFood = {
27
+ '獣肉' => Vec[10, 0, 0],
28
+ 'ウッチ' => Vec[ 0, 10, 10],
29
+ 'ゴッチ' => Vec[ 0, 12, 12],
30
+ 'ガガッチ' => Vec[ 0, 14, 14],
31
+ 'ドランギョ' => Vec[15, 15, 10],
32
+ 'ドラバーン' => Vec[20, 20, 15],
33
+ 'フレドラン' => Vec[50, 0, 0],
34
+ 'アースドラン' => Vec[ 0, 50, 0],
35
+ 'アクアドラン' => Vec[ 0, 0, 50],
36
+ 'ダークドン' => Vec[30, 30, 30],
37
+ }
38
+ SubFood = {
39
+ '氷酒' => Vec[ 50, 70, 50],
40
+ '氷水酒' => Vec[ 50, 90, 50],
41
+ '氷河酒' => Vec[ 50, 110, 50],
42
+ 'カエン酒' => Vec[ 70, 50, 70],
43
+ '爆炎酒' => Vec[ 90, 50, 90],
44
+ '煉獄酒' => Vec[110, 50, 110],
45
+ }
46
+ Cookery = {
47
+ '焼き' => Vec[50, 50, 30],
48
+ '蒸す' => Vec[30, 75, 75],
49
+ }
50
+ Cookery.store('丸焼き', Cookery['焼き'])
51
+ Cookery.store('すき焼き', Cookery['焼き'])
52
+ Cookery.store('焼く', Cookery['焼き'])
53
+ Cookery.store('焼', Cookery['焼き'])
54
+ Cookery.store('蒸し焼き', Cookery['蒸す'])
55
+ Cookery.store('ボイル', Cookery['蒸す'])
56
+ Cookery.store('蒸し', Cookery['蒸す'])
57
+ Cookery.store('蒸', Cookery['蒸す'])
58
+
59
+ class << self
60
+ def cook(cookery_s, main_s, sub_s, level)
61
+ begin
62
+ c = Cookery[cookery_s]
63
+ m = MainFood[main_s]
64
+ s = SubFood[sub_s]
65
+ v = Vec[1, 1, 1]
66
+ v.e_mul!(m).e_mul!(c).e_mul!(s.dup.add!(100+level)).e_div!(10000)
67
+ new(v)
68
+ rescue
69
+ arg = [cookery_s, main_s, sub_s, level].inspect
70
+ raise ArgumentError, "Some of arguments for cooking seems to be wrong. #{arg} is given, but they should be [cookery (String), main food (String), sub food (String), cooking level (Integer)]. Not all of cookeries and foods are supported."
71
+ end
72
+ end
73
+ end
25
74
  end
26
75
 
27
76
  SystemCuisine = {
@@ -72,55 +121,6 @@ module Mgmg
72
121
  end
73
122
  end
74
123
 
75
- MainFood = {
76
- '獣肉' => Vec[10, 0, 0],
77
- 'ウッチ' => Vec[ 0, 10, 10],
78
- 'ゴッチ' => Vec[ 0, 12, 12],
79
- 'ガガッチ' => Vec[ 0, 14, 14],
80
- 'ドランギョ' => Vec[15, 15, 10],
81
- 'ドラバーン' => Vec[20, 20, 15],
82
- 'フレドラン' => Vec[50, 0, 0],
83
- 'アースドラン' => Vec[ 0, 50, 0],
84
- 'アクアドラン' => Vec[ 0, 0, 50],
85
- 'ダークドン' => Vec[30, 30, 30],
86
- }
87
- SubFood = {
88
- '氷酒' => Vec[ 50, 70, 50],
89
- '氷水酒' => Vec[ 50, 90, 50],
90
- '氷河酒' => Vec[ 50, 110, 50],
91
- 'カエン酒' => Vec[ 70, 50, 70],
92
- '爆炎酒' => Vec[ 90, 50, 90],
93
- '煉獄酒' => Vec[110, 50, 110],
94
- }
95
- Cookery = {
96
- '焼き' => Vec[50, 50, 30],
97
- '蒸す' => Vec[30, 75, 75],
98
- }
99
- Cookery.store('丸焼き', Cookery['焼き'])
100
- Cookery.store('すき焼き', Cookery['焼き'])
101
- Cookery.store('焼く', Cookery['焼き'])
102
- Cookery.store('焼', Cookery['焼き'])
103
- Cookery.store('蒸し焼き', Cookery['蒸す'])
104
- Cookery.store('ボイル', Cookery['蒸す'])
105
- Cookery.store('蒸し', Cookery['蒸す'])
106
- Cookery.store('蒸', Cookery['蒸す'])
107
-
108
- class << Cuisine
109
- def cook(cookery_s, main_s, sub_s, level)
110
- begin
111
- c = Cookery[cookery_s]
112
- m = MainFood[main_s]
113
- s = SubFood[sub_s]
114
- v = Vec[1, 1, 1]
115
- v.e_mul!(m).e_mul!(c).e_mul!(s.dup.add!(100+level)).e_div!(10000)
116
- new(v)
117
- rescue
118
- arg = [cookery_s, main_s, sub_s, level].inspect
119
- raise ArgumentError, "Some of arguments for cooking seems to be wrong. #{arg} is given, but they should be [cookery (String), main food (String), sub food (String), cooking level (Integer)]. Not all of cookeries and foods are supported."
120
- end
121
- end
122
- end
123
-
124
124
  module_function def cuisine(*arg)
125
125
  case arg.size
126
126
  when 3
data/lib/mgmg/equip.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  module Mgmg
2
+ CacheMLS = Hash.new
2
3
  using Refiner
3
4
  class Equip
5
+ Cache, CacheML = Hash.new, Hash.new
4
6
  ParamList = %w|攻撃 物防 魔防 HP MP 腕力 器用 素早 魔力|
5
7
  ElementList = %w|火 地 水|
6
8
  EqPosList = %w|武 頭 胴 腕 足 飾|
@@ -196,195 +198,203 @@ module Mgmg
196
198
  Zero = self.new(28, 0, Vec.new(6, 0).freeze, 12, 12, Vec.new(9, 0).freeze, Vec.new(3, 0).freeze)
197
199
  Zero.total_cost.freeze; Zero.history.clear.freeze
198
200
  Zero.freeze
199
- end
200
-
201
- class << Equip
202
- def build(str, s_level, c_level, left_associative: true)
203
- str = Mgmg.check_string(str)
204
- stack, str = build_sub0([], str)
205
- build_sub(stack, str, s_level, c_level, left_associative)
206
- end
207
- private def build_sub0(stack, str)
208
- SystemEquip.each do |k, v|
209
- if SystemEquipRegexp[k].match(str)
210
- stack << v
211
- str = str.gsub(k, "<#{stack.length-1}>")
212
- end
201
+
202
+ class << self
203
+ def build(str, s_level, c_level, left_associative: true, include_system_equips: true)
204
+ str = Mgmg.check_string(str)
205
+ stack = []
206
+ stack, str = build_sub0(stack, str) if include_system_equips
207
+ build_sub(stack, str, s_level, c_level, left_associative)
213
208
  end
214
- [stack, str]
215
- end
216
- private def build_sub(stack, str, s_level, c_level, lassoc)
217
- if m = /\A(.*\+?)\[([^\[\]]+)\](\+?[^\[]*)\Z/.match(str)
218
- stack << build_sub(stack, m[2], s_level, c_level, lassoc)
219
- build_sub(stack, "#{m[1]}<#{stack.length-1}>#{m[3]}", s_level, c_level, lassoc)
220
- elsif m = ( lassoc ? /\A(.+)\+(.+?)\Z/ : /\A(.+?)\+(.+)\Z/ ).match(str)
221
- if c_level < 0
222
- compose(build_sub(stack, m[1], s_level, c_level, lassoc), build_sub(stack, m[2], s_level, c_level, lassoc), 0, true)
223
- else
224
- compose(build_sub(stack, m[1], s_level, c_level, lassoc), build_sub(stack, m[2], s_level, c_level, lassoc), c_level, false)
209
+ private def build_sub0(stack, str)
210
+ SystemEquip.each do |k, v|
211
+ if SystemEquipRegexp[k].match(str)
212
+ stack << v
213
+ str = str.gsub(k, "<#{stack.length-1}>")
214
+ end
225
215
  end
226
- elsif m = /\A\<(\d+)\>\Z/.match(str)
227
- stack[m[1].to_i]
228
- else
229
- if s_level < 0
230
- smith(str, 0, true)
216
+ [stack, str]
217
+ end
218
+ private def build_sub(stack, str, s_level, c_level, lassoc)
219
+ if m = /\A(.*\+?)\[([^\[\]]+)\](\+?[^\[]*)\Z/.match(str)
220
+ stack << build_sub(stack, m[2], s_level, c_level, lassoc)
221
+ build_sub(stack, "#{m[1]}<#{stack.length-1}>#{m[3]}", s_level, c_level, lassoc)
222
+ elsif m = ( lassoc ? /\A(.+)\+(.+?)\Z/ : /\A(.+?)\+(.+)\Z/ ).match(str)
223
+ if c_level < 0
224
+ compose(build_sub(stack, m[1], s_level, c_level, lassoc), build_sub(stack, m[2], s_level, c_level, lassoc), 0, true)
225
+ else
226
+ compose(build_sub(stack, m[1], s_level, c_level, lassoc), build_sub(stack, m[2], s_level, c_level, lassoc), c_level, false)
227
+ end
228
+ elsif m = /\A\<(\d+)\>\Z/.match(str)
229
+ stack[m[1].to_i]
231
230
  else
232
- smith(str, s_level, false)
231
+ if s_level < 0
232
+ smith(str, 0, true)
233
+ else
234
+ smith(str, s_level, false)
235
+ end
233
236
  end
234
237
  end
235
- end
236
-
237
- def compose(main, sub, level, outsourcing)
238
- main_k, sub_k = main.kind, sub.kind
239
- main_s, sub_s = main.star, sub.star
240
- main_main, sub_main = main.main, sub.main
241
- main_sub, sub_sub = main.sub, sub.sub
242
- para = Vec.new(9, 0)
243
- ele = Vec.new(3, 0)
244
-
245
- # 9パラメータ
246
- coef = Equip9[main_k].dup
247
- para[] = coef
248
- para.add!(level).e_div!(2)
249
- para.e_mul!(sub.para).e_div!(100)
250
- coef.sub!(Equip9[sub_k])
251
- coef.add!( 100 + (main_s-sub_s)*5 - ( ( main_main==sub_main && main_main != 9 ) ? 30 : 0 ) )
252
- coef.add!(Material9[main_main]).sub!(Material9[sub_main])
253
- coef.e_mul!(EquipFilter[main_k])
254
- para.e_mul!(coef).e_div!( main_k==sub_k ? 200 : 100 )
255
- para.add!(main.para)
256
238
 
257
- # エレメント
258
- ele[] = sub.element
259
- ele.e_mul!([75, level].min).e_div!( main_k==sub_k ? 200 : 100 )
260
- ele.add!(main.element)
261
-
262
- ret = new(main_k, main.weight+sub.weight, main_s+sub_s, main_sub, sub_main, para, ele)
263
- ret.total_cost.add!(main.total_cost).add!(sub.total_cost)
264
- cc = ret.comp_cost(outsourcing)
265
- ret.total_cost[1] += cc
266
- ret.total_cost[main_k < 8 ? 0 : 2] += cc
267
- ret.min_levels.merge!(main.min_levels, sub.min_levels)
268
- ret.history = [*main.history, *sub.history, ret]
269
- ret
270
- end
271
-
272
- def smith(str, level, outsourcing)
273
- str = Mgmg.check_string(str)
274
- unless m = /\A(.+)\((.+\d+),?(.+\d+)\)\Z/.match(str)
275
- raise InvalidSmithError.new(str)
276
- end
277
- kind = EquipIndex[m[1].to_sym]
278
- unless kind
279
- raise InvalidEquipClassError.new(m[1])
239
+ def compose(main, sub, level, outsourcing)
240
+ main_k, sub_k = main.kind, sub.kind
241
+ main_s, sub_s = main.star, sub.star
242
+ main_main, sub_main = main.main, sub.main
243
+ main_sub, sub_sub = main.sub, sub.sub
244
+ para = Vec.new(9, 0)
245
+ ele = Vec.new(3, 0)
246
+
247
+ # 9パラメータ
248
+ coef = Equip9[main_k].dup
249
+ para[] = coef
250
+ para.add!(level).e_div!(2)
251
+ para.e_mul!(sub.para).e_div!(100)
252
+ coef.sub!(Equip9[sub_k])
253
+ coef.add!( 100 + (main_s-sub_s)*5 - ( ( main_main==sub_main && main_main != 9 ) ? 30 : 0 ) )
254
+ coef.add!(Material9[main_main]).sub!(Material9[sub_main])
255
+ coef.e_mul!(EquipFilter[main_k])
256
+ para.e_mul!(coef).e_div!( main_k==sub_k ? 200 : 100 )
257
+ para.add!(main.para)
258
+
259
+ # エレメント
260
+ ele[] = sub.element
261
+ ele.e_mul!([75, level].min).e_div!( main_k==sub_k ? 200 : 100 )
262
+ ele.add!(main.element)
263
+
264
+ ret = new(main_k, main.weight+sub.weight, main_s+sub_s, main_sub, sub_main, para, ele)
265
+ ret.total_cost.add!(main.total_cost).add!(sub.total_cost)
266
+ cc = ret.comp_cost(outsourcing)
267
+ ret.total_cost[1] += cc
268
+ ret.total_cost[main_k < 8 ? 0 : 2] += cc
269
+ ret.min_levels.merge!(main.min_levels, sub.min_levels)
270
+ ret.history = [*main.history, *sub.history, ret]
271
+ ret
280
272
  end
281
- main_m, main_s, main_mc = Mgmg.parse_material(m[2])
282
- sub_m, sub_s, sub_mc = Mgmg.parse_material(m[3])
283
- para = Vec.new(9, 0)
284
- ele = Vec.new(3, 0)
285
273
 
286
- # 9パラメータ
287
- para[] = Equip9[kind]
288
- para.e_mul!(Main9[main_m]).e_div!(100)
289
- coef = Sub9[sub_m].dup
290
- coef.add!(level)
291
- para.e_mul!(coef).e_div!( main_mc==sub_mc ? 200 : 100 )
292
-
293
- # エレメント
294
- ele[] = MainEL[main_m]
295
- ele.e_mul!(SubEL[sub_m]).e_div!(6)
296
-
297
- # 重量
298
- weight = ( ( EquipWeight[kind] + SubWeight[sub_m] - level.div(2) ) * ( MainWeight[main_m] ) ).div(10000)
299
-
300
- ret = new(kind, ( weight<1 ? 1 : weight ), (main_s+sub_s).div(2), main_mc, sub_mc, para, ele)
301
- ret.total_cost[kind < 8 ? 0 : 2] += ret.smith_cost(outsourcing)
302
- ret.min_levels.store(str, Equip.min_level(str))
303
- ret
304
- end
305
-
306
- def min_level(str, weight=1)
307
- str = Mgmg.check_string(str)
308
- unless m = /\A(.+)\((.+\d+),?(.+\d+)\)\Z/.match(str)
309
- raise InvalidSmithError.new(str)
274
+ def smith(str, level, outsourcing)
275
+ str = Mgmg.check_string(str)
276
+ return Cache[str].dup if level==0 && Cache.has_key?(str)
277
+ unless m = /\A(.+)\((.+\d+),?(.+\d+)\)\Z/.match(str)
278
+ raise InvalidSmithError.new(str)
279
+ end
280
+ kind = EquipIndex[m[1].to_sym]
281
+ unless kind
282
+ raise InvalidEquipClassError.new(m[1])
283
+ end
284
+ main_m, main_s, main_mc = Mgmg.parse_material(m[2])
285
+ sub_m, sub_s, sub_mc = Mgmg.parse_material(m[3])
286
+ para = Vec.new(9, 0)
287
+ ele = Vec.new(3, 0)
288
+
289
+ # 9パラメータ
290
+ para[] = Equip9[kind]
291
+ para.e_mul!(Main9[main_m]).e_div!(100)
292
+ coef = Sub9[sub_m].dup
293
+ coef.add!(level)
294
+ para.e_mul!(coef).e_div!( main_mc==sub_mc ? 200 : 100 )
295
+
296
+ # エレメント
297
+ ele[] = MainEL[main_m]
298
+ ele.e_mul!(SubEL[sub_m]).e_div!(6)
299
+
300
+ # 重量
301
+ weight = ( ( EquipWeight[kind] + SubWeight[sub_m] - level.div(2) ) * ( MainWeight[main_m] ) ).div(10000)
302
+
303
+ ret = new(kind, ( weight<1 ? 1 : weight ), (main_s+sub_s).div(2), main_mc, sub_mc, para, ele)
304
+ ret.total_cost[kind < 8 ? 0 : 2] += ret.smith_cost(outsourcing)
305
+ ret.min_levels.store(str, Equip.min_level(str))
306
+ Cache.store(str, ret.freeze) if level==0
307
+ ret.dup
310
308
  end
311
- kind = EquipIndex[m[1].to_sym]
312
- main_m, main_s, = Mgmg.parse_material(m[2])
313
- sub_m, sub_s, = Mgmg.parse_material(m[3])
314
309
 
315
- q, r = ((weight+1)*10000).divmod(MainWeight[main_m])
316
- l = ( EquipWeight[kind] + SubWeight[sub_m] - q + ( r==0 ? 1 : 0 ) )*2
317
- [(main_s-1)*3, (sub_s-1)*3, l].max
318
- end
319
-
320
- def min_comp(str, opt: Option.new)
321
- str = Mgmg.check_string(str)
322
- stack, str = minc_sub0([], str)
323
- (minc_sub(stack, str, opt.left_associative)[1]-1)*3
324
- end
325
- private def minc_sub0(stack, str)
326
- SystemEquip.each do |k, v|
327
- if SystemEquipRegexp[k].match(str)
328
- stack << v.star
329
- str = str.gsub(k, "<#{stack.length-1}>")
310
+ def min_level(str, weight=1)
311
+ str = Mgmg.check_string(str)
312
+ return CacheML[str] if CacheML.has_key?(str)
313
+ unless m = /\A(.+)\((.+\d+),?(.+\d+)\)\Z/.match(str)
314
+ raise InvalidSmithError.new(str)
330
315
  end
316
+ kind = EquipIndex[m[1].to_sym]
317
+ main_m, main_s, = Mgmg.parse_material(m[2])
318
+ sub_m, sub_s, = Mgmg.parse_material(m[3])
319
+
320
+ q, r = ((weight+1)*10000).divmod(MainWeight[main_m])
321
+ l = ( EquipWeight[kind] + SubWeight[sub_m] - q + ( r==0 ? 1 : 0 ) )*2
322
+ ret = [(main_s-1)*3, (sub_s-1)*3, l].max
323
+ CacheML.store(str, ret)
324
+ ret
331
325
  end
332
- [stack, str]
333
- end
334
- private def minc_sub(stack, str, lassoc)
335
- if m = /\A(.*\+?)\[([^\[\]]+)\](\+?[^\[]*)\Z/.match(str)
336
- stack << minc_sub(stack, m[2], lassoc)[0]
337
- minc_sub(stack, "#{m[1]}<#{stack.length-1}>#{m[3]}", lassoc)
338
- elsif m = ( lassoc ? /\A(.+)\+(.+?)\Z/ : /\A(.+?)\+(.+)\Z/ ).match(str)
339
- a, _ = minc_sub(stack, m[1], lassoc)
340
- b, _ = minc_sub(stack, m[2], lassoc)
341
- [a+b, [a, b].max]
342
- elsif m = /\A\<(\d+)\>\Z/.match(str)
343
- [stack[m[1].to_i], 1]
344
- else
345
- [smith(str, 0, true).star, 1]
326
+
327
+ def min_comp(str, opt: Option.new)
328
+ str = Mgmg.check_string(str)
329
+ stack = []
330
+ stack, str = minc_sub0(stack, str) if opt.include_system_equips
331
+ (minc_sub(stack, str, opt.left_associative)[1]-1)*3
346
332
  end
347
- end
348
-
349
- def min_smith(str, opt: Option.new)
350
- str = Mgmg.check_string(str)
351
- stack, str = mins_sub0([], str)
352
- ret = (([mins_sub(stack, str, opt.left_associative)]+stack).max-1)*3
353
- ret < 0 ? -1 : ret
354
- end
355
- private def mins_sub0(stack, str)
356
- SystemEquip.each do |k, v|
357
- if SystemEquipRegexp[k].match(str)
358
- stack << 0
359
- str = str.gsub(k, "<#{stack.length-1}>")
333
+ private def minc_sub0(stack, str)
334
+ SystemEquip.each do |k, v|
335
+ if SystemEquipRegexp[k].match(str)
336
+ stack << v.star
337
+ str = str.gsub(k, "<#{stack.length-1}>")
338
+ end
360
339
  end
340
+ [stack, str]
361
341
  end
362
- [stack, str]
363
- end
364
- private def mins_sub(stack, str, lassoc)
365
- if m = /\A(.*\+?)\[([^\[\]]+)\](\+?[^\[]*)\Z/.match(str)
366
- stack << mins_sub(stack, m[2], lassoc)
367
- mins_sub(stack, "#{m[1]}<#{stack.length-1}>#{m[3]}", lassoc)
368
- elsif m = ( lassoc ? /\A(.+)\+(.+?)\Z/ : /\A(.+?)\+(.+)\Z/ ).match(str)
369
- [mins_sub(stack, m[1], lassoc), mins_sub(stack, m[2], lassoc)].max
370
- elsif m = /\A\<(\d+)\>\Z/.match(str)
371
- 0
372
- else
373
- mins_sub2(str)
342
+ private def minc_sub(stack, str, lassoc)
343
+ if m = /\A(.*\+?)\[([^\[\]]+)\](\+?[^\[]*)\Z/.match(str)
344
+ stack << minc_sub(stack, m[2], lassoc)[0]
345
+ minc_sub(stack, "#{m[1]}<#{stack.length-1}>#{m[3]}", lassoc)
346
+ elsif m = ( lassoc ? /\A(.+)\+(.+?)\Z/ : /\A(.+?)\+(.+)\Z/ ).match(str)
347
+ a, _ = minc_sub(stack, m[1], lassoc)
348
+ b, _ = minc_sub(stack, m[2], lassoc)
349
+ [a+b, [a, b].max]
350
+ elsif m = /\A\<(\d+)\>\Z/.match(str)
351
+ [stack[m[1].to_i], 1]
352
+ else
353
+ [smith(str, 0, true).star, 1]
354
+ end
374
355
  end
375
- end
376
- private def mins_sub2(str)
377
- str = Mgmg.check_string(str)
378
- unless m = /\A(.+)\((.+\d+),?(.+\d+)\)\Z/.match(str)
379
- raise InvalidSmithError.new(str)
356
+
357
+ def min_smith(str, opt: Option.new)
358
+ str = Mgmg.check_string(str)
359
+ stack = []
360
+ stack, str = mins_sub0(stack, str) if opt.include_system_equips
361
+ ret = (([mins_sub(stack, str, opt.left_associative)]+stack).max-1)*3
362
+ ret < 0 ? -1 : ret
380
363
  end
381
- kind = EquipIndex[m[1].to_sym]
382
- unless kind
383
- raise InvalidEquipClassError.new(m[1])
364
+ private def mins_sub0(stack, str)
365
+ SystemEquip.each do |k, v|
366
+ if SystemEquipRegexp[k].match(str)
367
+ stack << 0
368
+ str = str.gsub(k, "<#{stack.length-1}>")
369
+ end
370
+ end
371
+ [stack, str]
372
+ end
373
+ private def mins_sub(stack, str, lassoc)
374
+ if m = /\A(.*\+?)\[([^\[\]]+)\](\+?[^\[]*)\Z/.match(str)
375
+ stack << mins_sub(stack, m[2], lassoc)
376
+ mins_sub(stack, "#{m[1]}<#{stack.length-1}>#{m[3]}", lassoc)
377
+ elsif m = ( lassoc ? /\A(.+)\+(.+?)\Z/ : /\A(.+?)\+(.+)\Z/ ).match(str)
378
+ [mins_sub(stack, m[1], lassoc), mins_sub(stack, m[2], lassoc)].max
379
+ elsif m = /\A\<(\d+)\>\Z/.match(str)
380
+ 0
381
+ else
382
+ mins_sub2(str)
383
+ end
384
+ end
385
+ private def mins_sub2(str)
386
+ str = Mgmg.check_string(str)
387
+ unless m = /\A(.+)\((.+\d+),?(.+\d+)\)\Z/.match(str)
388
+ raise InvalidSmithError.new(str)
389
+ end
390
+ kind = EquipIndex[m[1].to_sym]
391
+ unless kind
392
+ raise InvalidEquipClassError.new(m[1])
393
+ end
394
+ main_m, main_s, main_mc = Mgmg.parse_material(m[2])
395
+ sub_m, sub_s, sub_mc = Mgmg.parse_material(m[3])
396
+ [main_s, sub_s].max
384
397
  end
385
- main_m, main_s, main_mc = Mgmg.parse_material(m[2])
386
- sub_m, sub_s, sub_mc = Mgmg.parse_material(m[3])
387
- [main_s, sub_s].max
388
398
  end
389
399
  end
390
400
  end