mgmg 1.5.2 → 1.5.5

Sign up to get free protection for your applications and to get access to all the features.
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