mgmg 1.5.2 → 1.5.5

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: 708daf16885a7532d26106458fbcabf1e043bfbbc0d6c8607af45a2e5b31ca45
4
- data.tar.gz: 765e3c12305233d28ea32c8c13ea819bfe52849a393cc5dc64d3c4e54fbe3b70
3
+ metadata.gz: dcdae95e0d1ee5dc8d65fe012d739a896b5be27e2a68ff5a2619477461e2e9d7
4
+ data.tar.gz: 2a2976e6c36a2cd2c3c6bc7c6e829f7e78e2791d18bc087188b369db7ecacb17
5
5
  SHA512:
6
- metadata.gz: d673f9054fe9a1c9ec2d771a699046eb457ddfbc08306355d2a64b988fd00b841e29edc8da7b9c00305423a255ca322a559eb7e8931463ef4ce6ebec63679ce8
7
- data.tar.gz: 1b2374ffa189136a4b4036424a77660eef55401be88e06a00f9f6034354ae7ecfdf24eeb2b889c1f0df3049fb3978e63bb5ddb355acf4d165023feb9186446bb
6
+ metadata.gz: d40a50cc4cde3a115d7d9f27a93945a27f4b77ece5091917d30cf32675679f6ae52290163168cd5d8c3cf57ded10f29bbb3a6fc803e67d209c35cf8c949f3912
7
+ data.tar.gz: 27f15eef233a9f19a92bca31d4b4683e78fb1207768fd7b8cbc7cc2aab8d6d79c23ac3db93237a0e8b508dfaa4f50da745f001de50ce74bf5d34824ee3909dc6
data/CHANGELOG.md CHANGED
@@ -141,3 +141,18 @@
141
141
 
142
142
  ## 1.5.2 2022/06/28
143
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`を追加.
155
+
156
+ ## 1.5.5 2022/07/02
157
+ - `Enumerable#to_recipe`が動かなくなったバグを修正.
158
+ - `String#min_level`において,正しい答えを返さなくなったバグを修正.
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,204 @@ 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
+ key = [str.dup.freeze, weight].freeze
313
+ return CacheML[key] if CacheML.has_key?(key)
314
+ unless m = /\A(.+)\((.+\d+),?(.+\d+)\)\Z/.match(str)
315
+ raise InvalidSmithError.new(str)
330
316
  end
317
+ kind = EquipIndex[m[1].to_sym]
318
+ main_m, main_s, = Mgmg.parse_material(m[2])
319
+ sub_m, sub_s, = Mgmg.parse_material(m[3])
320
+
321
+ q, r = ((weight+1)*10000).divmod(MainWeight[main_m])
322
+ l = ( EquipWeight[kind] + SubWeight[sub_m] - q + ( r==0 ? 1 : 0 ) )*2
323
+ ret = [(main_s-1)*3, (sub_s-1)*3, l].max
324
+ CacheML.store(key, ret)
325
+ ret
331
326
  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]
327
+
328
+ def min_comp(str, opt: Option.new)
329
+ str = Mgmg.check_string(str)
330
+ stack = []
331
+ stack, str = minc_sub0(stack, str) if opt.include_system_equips
332
+ (minc_sub(stack, str, opt.left_associative)[1]-1)*3
346
333
  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}>")
334
+ private def minc_sub0(stack, str)
335
+ SystemEquip.each do |k, v|
336
+ if SystemEquipRegexp[k].match(str)
337
+ stack << v.star
338
+ str = str.gsub(k, "<#{stack.length-1}>")
339
+ end
360
340
  end
341
+ [stack, str]
361
342
  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)
343
+ private def minc_sub(stack, str, lassoc)
344
+ if m = /\A(.*\+?)\[([^\[\]]+)\](\+?[^\[]*)\Z/.match(str)
345
+ stack << minc_sub(stack, m[2], lassoc)[0]
346
+ minc_sub(stack, "#{m[1]}<#{stack.length-1}>#{m[3]}", lassoc)
347
+ elsif m = ( lassoc ? /\A(.+)\+(.+?)\Z/ : /\A(.+?)\+(.+)\Z/ ).match(str)
348
+ a, _ = minc_sub(stack, m[1], lassoc)
349
+ b, _ = minc_sub(stack, m[2], lassoc)
350
+ [a+b, [a, b].max]
351
+ elsif m = /\A\<(\d+)\>\Z/.match(str)
352
+ [stack[m[1].to_i], 1]
353
+ else
354
+ [smith(str, 0, true).star, 1]
355
+ end
374
356
  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)
357
+
358
+ def min_smith(str, opt: Option.new)
359
+ str = Mgmg.check_string(str)
360
+ stack = []
361
+ stack, str = mins_sub0(stack, str) if opt.include_system_equips
362
+ ret = (([mins_sub(stack, str, opt.left_associative)]+stack).max-1)*3
363
+ ret < 0 ? -1 : ret
380
364
  end
381
- kind = EquipIndex[m[1].to_sym]
382
- unless kind
383
- raise InvalidEquipClassError.new(m[1])
365
+ private def mins_sub0(stack, str)
366
+ SystemEquip.each do |k, v|
367
+ if SystemEquipRegexp[k].match(str)
368
+ stack << 0
369
+ str = str.gsub(k, "<#{stack.length-1}>")
370
+ end
371
+ end
372
+ [stack, str]
373
+ end
374
+ private def mins_sub(stack, str, lassoc)
375
+ if m = /\A(.*\+?)\[([^\[\]]+)\](\+?[^\[]*)\Z/.match(str)
376
+ stack << mins_sub(stack, m[2], lassoc)
377
+ mins_sub(stack, "#{m[1]}<#{stack.length-1}>#{m[3]}", lassoc)
378
+ elsif m = ( lassoc ? /\A(.+)\+(.+?)\Z/ : /\A(.+?)\+(.+)\Z/ ).match(str)
379
+ [mins_sub(stack, m[1], lassoc), mins_sub(stack, m[2], lassoc)].max
380
+ elsif m = /\A\<(\d+)\>\Z/.match(str)
381
+ 0
382
+ else
383
+ mins_sub2(str)
384
+ end
385
+ end
386
+ private def mins_sub2(str)
387
+ str = Mgmg.check_string(str)
388
+ unless m = /\A(.+)\((.+\d+),?(.+\d+)\)\Z/.match(str)
389
+ raise InvalidSmithError.new(str)
390
+ end
391
+ kind = EquipIndex[m[1].to_sym]
392
+ unless kind
393
+ raise InvalidEquipClassError.new(m[1])
394
+ end
395
+ main_m, main_s, main_mc = Mgmg.parse_material(m[2])
396
+ sub_m, sub_s, sub_mc = Mgmg.parse_material(m[3])
397
+ [main_s, sub_s].max
384
398
  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
399
  end
389
400
  end
390
401
  end