mgmg 1.5.7 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 959345a0547c379e6ea2cc55e56283b580429c6b25d2a50df0c3ac9c7b02f8db
4
- data.tar.gz: 8832f8ad1f54c1f06c77a2c72b25631fbfa9e9f98b5fe0d48ae15c7dd15568da
3
+ metadata.gz: b9da45e92b9c5fb1cae6577e532cce280c997f46c1df019514b1eb976d89f03f
4
+ data.tar.gz: 6f905a8d05126dbc81c71e73ddd64988e96a2033f520f6ded8b595ac96eb06be
5
5
  SHA512:
6
- metadata.gz: d22e5011de865ee5a40550ca1341ae7f5c4ea696f086c95e9ce7be273cd2c4d1acbd6b676c1e87371fc1af05bfd90c1dc796d2b3076a3f5f7a01c1336bed7856
7
- data.tar.gz: a70bd19a98d4212ff63d535be59b528d054dc4cbf981eda4bed20ef9e4dce065bc48f7aa4a15e9e6b90542872dde0c3537558557ce3ad1e4129ab174ceb84b8b
6
+ metadata.gz: cfaf9d22a5aaa36fdef9c15348117a014a4a9385947417a614a3f1fc82622ee4f1ad4c0d5615cbde9dded71b4a2a13edb88938e0fc88c818fafe27625b8f9885
7
+ data.tar.gz: 46734977a74542248d65449eb9b49aa4c9ce6f5b875a194c1ec8db6bdab47856fa341c9c77ef29f28c76dd4d078899260da64a6c8d04510fd7b4c692af8581c1
data/CHANGELOG.md CHANGED
@@ -165,3 +165,12 @@
165
165
  ## 1.5.7 2022/10/15
166
166
  - 経験値上限を指定して,目標パラメータを最大化する製作Lvを返す`Mgmg::Recipe#find_max`を追加.
167
167
  - `Mgmg::Recipe#search`において,解の経験値が`cut_exp`ちょうどの場合に例外となっていたバグを修正.
168
+
169
+ ## 1.6.0 2022/10/18
170
+ - `Mgmg.#find_upperbound`のアルゴリズムを改善し,探索下限目標値の引数を削除.
171
+ - `Enumerable#search`,`Enumerable#find_max`が正しい解を返さない場合があったバグを修正.
172
+
173
+ ## 1.6.1 2022/10/21
174
+ - `Mgmg.#find_lowerbound`,`Mgmg.#find_upperbound`において,同値レシピを指定した場合などを例外とするように修正.
175
+ - `Enumerable#to_recipe`にも`allow_over20`キーワード引数を導入し,デフォルトで☆20を超えるレシピを含む場合に例外とするように修正.
176
+ - 実効HP(最大HP+腕力)を表す`Mgmg::Equip#hs`を追加.
data/lib/mgmg/equip.rb CHANGED
@@ -105,6 +105,9 @@ module Mgmg
105
105
  def magic2
106
106
  magic()*2
107
107
  end
108
+ def hs
109
+ hp()+str()
110
+ end
108
111
  [:fire, :earth, :water].each.with_index do |s, i|
109
112
  define_method(s){ @element[i] }
110
113
  end
data/lib/mgmg/ir.rb CHANGED
@@ -220,6 +220,9 @@ module Mgmg
220
220
  def pmdef(s, ac, x=nil)
221
221
  [phydef(s, ac, x), magmag(s, ac, x)].min
222
222
  end
223
+ def hs(s, ac, x=nil)
224
+ hp(s, ac, x)+str(s, ac, x)
225
+ end
223
226
 
224
227
  def power(s, a=s, c=a.tap{a=s})
225
228
  case @kind
data/lib/mgmg/option.rb CHANGED
@@ -2,7 +2,7 @@ module Mgmg
2
2
  class Option
3
3
  Defaults = {
4
4
  left_associative: true, include_system_equips: true,
5
- smith_max: 10000, armor_max: 10000, comp_max: 10000
5
+ smith_max: 10_000, armor_max: 10_000, comp_max: 10_000
6
6
  }
7
7
  def initialize(
8
8
  left_associative: Defaults[:left_associative],
data/lib/mgmg/recipe.rb CHANGED
@@ -130,7 +130,7 @@ module Mgmg
130
130
  def ir(**kw)
131
131
  temp_opt(**kw).irep
132
132
  end
133
- %i|attack phydef magdef hp mp str dex speed magic atkstr atk_sd dex_as mag_das magic2 magmag pmdef|.each do |sym|
133
+ %i|attack phydef magdef hp mp str dex speed magic atkstr atk_sd dex_as mag_das magic2 magmag pmdef hs|.each do |sym|
134
134
  define_method(sym) do |s, ac=s, x=false, **kw|
135
135
  s, ac, x = correct_level(s, ac, x, temp_opt(**kw))
136
136
  ir(**kw).method(sym).call(s, ac, x)
data/lib/mgmg/search.rb CHANGED
@@ -79,15 +79,27 @@ class String
79
79
  ret
80
80
  end
81
81
 
82
+ private def minimize_smith(para, smith, comp, cur, opt)
83
+ (smith-1).downto(opt.smith_min) do |s|
84
+ foo = opt.irep.para_call(para, s, comp)
85
+ if cur == foo
86
+ smith = s
87
+ else
88
+ break
89
+ end
90
+ end
91
+ smith
92
+ end
82
93
  def find_max(para, max_exp, opt: Mgmg::Option.new)
83
94
  opt = opt.dup.set_default(self)
84
95
  exp = Mgmg.exp(opt.smith_min, opt.comp_min)
85
96
  raise Mgmg::SearchCutException, "the recipe requires #{exp.comma3} experiment points, which exceeds given max_exp=#{max_exp.comma3}" if max_exp < exp
86
97
  ret = [Mgmg.invexp2(max_exp, opt.comp_min), opt.comp_min]
87
98
  max = opt.irep.para_call(para, *ret)
88
- (opt.comp_min+1).step(Mgmg.invexp2c(max_exp, opt.smith_min), opt.step) do |comp|
99
+ (opt.comp_min+1).upto(Mgmg.invexp2c(max_exp, opt.smith_min)) do |comp|
89
100
  smith = Mgmg.invexp2(max_exp, comp)
90
101
  cur = opt.irep.para_call(para, smith, comp)
102
+ smith = minimize_smith(para, smith, comp, cur, opt) if max <= cur
91
103
  ret, max = [smith, comp], cur if ( max < cur || ( max == cur && Mgmg.exp(smith, comp) < Mgmg.exp(*ret) ) )
92
104
  end
93
105
  ret
@@ -211,20 +223,79 @@ module Enumerable
211
223
  end
212
224
  opt.comp_max
213
225
  end
226
+ private def search_aonly(para, target, opt: Mgmg::Option.new)
227
+ opt_nocut = opt.dup; opt_nocut.cut_exp = Float::INFINITY
228
+ opt.armor_max = armor_search(para, target, -1, opt.comp_min, opt: opt_nocut)
229
+ opt.armor_min = armor_search(para, target, -1, opt.comp_max, opt: opt_nocut)
230
+ raise Mgmg::SearchCutException if opt.cut_exp < Mgmg.exp(opt.armor_min, opt.comp_min)
231
+ opt.comp_max = comp_search(para, target, -1, opt.armor_min, opt:)
232
+ ret = nil
233
+ exp = Mgmg.exp(opt.armor_min, opt.comp_max)
234
+ opt.cut_exp, ret = exp, [-1, opt.armor_min, opt.comp_max] if exp <= opt.cut_exp
235
+ exp = Mgmg.exp(opt.armor_max, opt.comp_min)
236
+ opt.cut_exp, ret = exp, [-1, opt.armor_max, opt.comp_min] if ( exp < opt.cut_exp || (ret.nil? && exp==opt.cut_exp) )
237
+ (opt.comp_min+opt.step).step(opt.comp_max-1, opt.step) do |comp|
238
+ break if opt.cut_exp < Mgmg.exp(opt.armor_min, comp)
239
+ armor = armor_search(para, target, -1, comp, opt:)
240
+ exp = Mgmg.exp(armor, comp)
241
+ if exp < opt.cut_exp
242
+ opt.cut_exp, ret = exp, [-1, armor, comp]
243
+ elsif exp == opt.cut_exp
244
+ if ret.nil? or opt.irep.para_call(para, *ret) < opt.irep.para_call(para, -1, armor, comp) then
245
+ ret = [-1, armor, comp]
246
+ end
247
+ end
248
+ rescue Mgmg::SearchCutException
249
+ end
250
+ if ret.nil?
251
+ max = opt.irep.para_call(para, *find_max(para, opt.cut_exp, opt:))
252
+ raise Mgmg::SearchCutException, "the maximum output with given cut_exp=#{opt.cut_exp.comma3} is #{max.comma3}, which does not reach given target=#{target.comma3}"
253
+ end
254
+ ret
255
+ end
256
+ private def search_sonly(para, target, opt: Mgmg::Option.new)
257
+ opt_nocut = opt.dup; opt_nocut.cut_exp = Float::INFINITY
258
+ opt.smith_max = smith_search(para, target, -1, opt.comp_min, opt: opt_nocut)
259
+ opt.smith_min = smith_search(para, target, -1, opt.comp_max, opt: opt_nocut)
260
+ raise Mgmg::SearchCutException if opt.cut_exp < Mgmg.exp(opt.smith_min, opt.comp_min)
261
+ opt.comp_max = comp_search(para, target, opt.smith_min, -1, opt:)
262
+ ret = nil
263
+ exp = Mgmg.exp(opt.smith_min, opt.comp_max)
264
+ opt.cut_exp, ret = exp, [opt.smith_min, -1, opt.comp_max] if exp <= opt.cut_exp
265
+ exp = Mgmg.exp(opt.smith_max, opt.comp_min)
266
+ opt.cut_exp, ret = exp, [opt.smith_max, -1, opt.comp_min] if ( exp < opt.cut_exp || (ret.nil? && exp==opt.cut_exp) )
267
+ (opt.comp_min+opt.step).step(opt.comp_max-1, opt.step) do |comp|
268
+ break if opt.cut_exp < Mgmg.exp(opt.smith_min, comp)
269
+ smith = smith_search(para, target, -1, comp, opt:)
270
+ exp = Mgmg.exp(smith, comp)
271
+ if exp < opt.cut_exp
272
+ opt.cut_exp, ret = exp, [smith, -1, comp]
273
+ elsif exp == opt.cut_exp
274
+ if ret.nil? or opt.irep.para_call(para, *ret) < opt.irep.para_call(para, smith, -1, comp) then
275
+ ret = [smith, -1, comp]
276
+ end
277
+ end
278
+ rescue Mgmg::SearchCutException
279
+ end
280
+ if ret.nil?
281
+ max = opt.irep.para_call(para, *find_max(para, opt.cut_exp, opt:))
282
+ raise Mgmg::SearchCutException, "the maximum output with given cut_exp=#{opt.cut_exp.comma3} is #{max.comma3}, which does not reach given target=#{target.comma3}"
283
+ end
284
+ ret
285
+ end
214
286
  def search(para, target, opt: Mgmg::Option.new)
215
287
  opt = opt.dup.set_default(self)
216
288
  opt.comp_min = comp_search(para, target, opt.smith_max, opt.armor_max, opt:)
217
289
  opt_nocut = opt.dup; opt_nocut.cut_exp = Float::INFINITY
218
- opt.smith_max, opt.armor_max = sa_search(para, target, opt.comp_min, opt: opt_nocut)
219
- opt.smith_min, opt.armor_min = sa_search(para, target, opt.comp_max, opt: opt_nocut)
290
+ opt.smith_max = smith_search(para, target, opt.armor_min, opt.comp_min, opt: opt_nocut) rescue ( return search_aonly(para, target, opt:) )
291
+ opt.armor_max = armor_search(para, target, opt.smith_min, opt.comp_min, opt: opt_nocut) rescue ( return search_sonly(para, target, opt:) )
292
+ opt.smith_min = smith_search(para, target, opt.armor_max, opt.comp_max, opt: opt_nocut)
293
+ opt.armor_min = armor_search(para, target, opt.smith_max, opt.comp_max, opt: opt_nocut)
220
294
  raise Mgmg::SearchCutException if opt.cut_exp < Mgmg.exp(opt.smith_min, opt.armor_min, opt.comp_min)
221
295
  opt.comp_max = comp_search(para, target, opt.smith_min, opt.armor_min, opt:)
222
- ret = nil
223
296
  exp = Mgmg.exp(opt.smith_min, opt.armor_min, opt.comp_max)
224
297
  opt.cut_exp, ret = exp, [opt.smith_min, opt.armor_min,opt. comp_max] if exp <= opt.cut_exp
225
- exp = Mgmg.exp(opt.smith_max, opt.armor_max, opt.comp_min)
226
- opt.cut_exp, ret = exp, [opt.smith_max, opt.armor_max, opt.comp_min] if ( exp < opt.cut_exp || (ret.nil? && exp==opt.cut_exp) )
227
- (opt.comp_min+1).upto(opt.comp_max-1) do |comp|
298
+ (opt.comp_min).upto(opt.comp_max-1) do |comp|
228
299
  break if opt.cut_exp < Mgmg.exp(opt.smith_min, opt.armor_min, comp)
229
300
  smith, armor = sa_search(para, target, comp, opt:)
230
301
  exp = Mgmg.exp(smith, armor, comp)
@@ -244,17 +315,31 @@ module Enumerable
244
315
  ret
245
316
  end
246
317
 
318
+ private def minimize_smith(para, smith, armor, comp, cur, opt)
319
+ return -1 if opt.smith_min < 0
320
+ (smith-1).downto(opt.smith_min) do |s|
321
+ foo = opt.irep.para_call(para, s, armor, comp)
322
+ if cur == foo
323
+ smith = s
324
+ else
325
+ break
326
+ end
327
+ end
328
+ smith
329
+ end
247
330
  def find_max(para, max_exp, opt: Mgmg::Option.new)
248
331
  opt = opt.dup.set_default(self)
249
332
  exp = Mgmg.exp(opt.smith_min, opt.armor_min, opt.comp_min)
250
333
  raise Mgmg::SearchCutException, "the recipe requires #{exp.comma3} experiment points, which exceeds given max_exp=#{max_exp.comma3}" if max_exp < exp
251
334
  ret = [Mgmg.invexp3(max_exp, opt.armor_min, opt.comp_min), opt.armor_min, opt.comp_min]
252
335
  max = opt.irep.para_call(para, *ret)
253
- (opt.comp_min+1).step(Mgmg.invexp3c(max_exp, opt.smith_min, opt.armor_min), opt.step) do |comp|
336
+ (opt.comp_min).step(Mgmg.invexp3c(max_exp, opt.smith_min, opt.armor_min), opt.step) do |comp|
254
337
  opt.armor_min.upto(Mgmg.invexp3(max_exp, opt.smith_min, comp)) do |armor|
255
338
  smith = Mgmg.invexp3(max_exp, armor, comp)
256
339
  cur = opt.irep.para_call(para, smith, armor, comp)
340
+ smith = minimize_smith(para, smith, armor, comp, cur, opt) if max <= cur
257
341
  ret, max = [smith, armor, comp], cur if ( max < cur || ( max == cur && Mgmg.exp(smith, armor, comp) < Mgmg.exp(*ret) ) )
342
+ break if armor < 0
258
343
  end
259
344
  end
260
345
  ret
@@ -281,38 +366,49 @@ module Mgmg
281
366
  end
282
367
  sca, scb = a.search(para, start, opt: opt_a), b.search(para, start, opt: opt_b)
283
368
  ea, eb = Mgmg.exp(*sca), Mgmg.exp(*scb)
284
- if eb < ea || ( ea == eb && opt_a.irep.para_call(para, *sca) < opt_b.irep.para_call(para, *scb) )
369
+ pa, pb = opt_a.irep.para_call(para, *sca), opt_b.irep.para_call(para, *scb)
370
+ if eb < ea || ( ea == eb && pa < pb )
285
371
  a, b, opt_a, opt_b, sca, scb, ea, eb = b, a, opt_b, opt_a, scb, sca, eb, ea
286
- end
287
- tag = opt_a.irep.para_call(para, *sca) + Eighth
288
- sca, scb = a.search(para, term, opt: opt_a), b.search(para, term, opt: opt_b)
289
- ea, eb = Mgmg.exp(*sca), Mgmg.exp(*scb)
290
- if ea < eb || ( ea == eb && opt_b.irep.para_call(para, *scb) < opt_a.irep.para_call(para, *sca) )
291
- raise Mgmg::SearchCutException
292
- end
293
- while tag < term
294
- sca, scb = a.search(para, tag, opt: opt_a), b.search(para, tag, opt: opt_b)
295
- ea, eb = Mgmg.exp(*sca), Mgmg.exp(*scb)
296
- pa, pb = opt_a.irep.para_call(para, *sca), opt_b.irep.para_call(para, *scb)
297
- if eb < ea
298
- return [(tag-Eighth).to_ii, pb]
299
- elsif ea == eb
300
- if pa < pb
301
- return [(tag-Eighth).to_ii, pa]
302
- else
303
- tag = pb + Eighth
304
- end
305
- else
372
+ elsif eb == ea && pa == pb
373
+ raise Mgmg::SearchCutException, "given recipes are equivalent at start target=#{start.comma3}"
374
+ end
375
+ scat, scbt = a.search(para, term, opt: opt_a), b.search(para, term, opt: opt_b)
376
+ eat, ebt = Mgmg.exp(*scat), Mgmg.exp(*scbt)
377
+ if eat < ebt || ( eat == ebt && opt_b.irep.para_call(para, *scbt) <= opt_a.irep.para_call(para, *scat) )
378
+ raise Mgmg::SearchCutException, "given recipes will never be reversed from start target=#{start.comma3} until term target=#{term.comma3}"
379
+ end
380
+
381
+ loop do
382
+ loop do
383
+ foo = a.find_max(para, eb, opt: opt_a)
384
+ break if sca == foo
385
+ sca, pa = foo, opt_a.irep.para_call(para, *foo)
386
+ scb = b.search(para, pa, opt: opt_b)
387
+ foo = Mgmg.exp(*scb)
388
+ break if eb == foo
389
+ eb = foo
390
+ end
391
+ ea = Mgmg.exp(*sca)
392
+ while ea<=eb
306
393
  tag = pa + Eighth
394
+ raise Mgmg::SearchCutException, "given recipes are never reversed from start target=#{start.comma3} until term target=#{term.comma3}" if term < tag
395
+ sca, scb = a.search(para, tag, opt: opt_a), b.search(para, tag, opt: opt_b)
396
+ ea, eb = Mgmg.exp(*sca), Mgmg.exp(*scb)
397
+ pa, pb = opt_a.irep.para_call(para, *sca), opt_b.irep.para_call(para, *scb)
398
+ break if ea == eb && pa < pb
399
+ end
400
+ if eb < ea || ( ea == eb && pa < pb )
401
+ until ea < eb || ( ea == eb && pb < pa )
402
+ sca = a.find_max(para, ea-1, opt: opt_a)
403
+ ea, pa = Mgmg.exp(*sca), opt_a.irep.para_call(para, *sca)
404
+ end
405
+ return [pa, pb]
307
406
  end
308
407
  end
309
408
  raise UnexpectedError
310
409
  end
311
410
 
312
- module_function def find_upperbound(a, b, para, start, term, opt_a: Option.new, opt_b: Option.new)
313
- if start <= term
314
- raise ArgumentError, "term < start is needed, (start, term) = (#{start}, #{term}) are given"
315
- end
411
+ module_function def find_upperbound(a, b, para, start, opt_a: Option.new, opt_b: Option.new)
316
412
  if a.kind_of?(Recipe)
317
413
  opt_a = a.option.dup
318
414
  a = a.recipe
@@ -327,58 +423,58 @@ module Mgmg
327
423
  end
328
424
  sca, scb = a.search(para, start, opt: opt_a), b.search(para, start, opt: opt_b)
329
425
  ea, eb = Mgmg.exp(*sca), Mgmg.exp(*scb)
330
- if ea < eb || ( ea == eb && opt_b.irep.para_call(para, *scb) < opt_a.irep.para_call(para, *sca) )
426
+ pa, pb = opt_a.irep.para_call(para, *sca), opt_b.irep.para_call(para, *scb)
427
+ if ea < eb || ( ea == eb && pb < pa )
331
428
  a, b, opt_a, opt_b, sca, scb, ea, eb = b, a, opt_b, opt_a, scb, sca, eb, ea
332
- end
333
- tagu = opt_a.irep.para_call(para, *sca)
334
- sca[-1] -= 2
335
- tagl = opt_a.irep.para_call(para, *sca)
336
- sca, scb = a.search(para, term, opt: opt_a), b.search(para, term, opt: opt_b)
337
- ea, eb = Mgmg.exp(*sca), Mgmg.exp(*scb)
338
- if eb < ea || ( ea == eb && opt_a.irep.para_call(para, *sca) < opt_b.irep.para_call(para, *scb) )
339
- raise Mgmg::SearchCutException
340
- end
341
- while term < tagu
342
- ret = nil
343
- sca = a.search(para, tagl, opt: opt_a)
344
- next_tagu, next_sca = tagl, sca
345
- scb = b.search(para, tagl, opt: opt_b)
346
- while tagl < tagu
429
+ elsif eb == ea && pa == pb
430
+ raise Mgmg::SearchCutException, "given recipes are equivalent at start target=#{start.comma3}"
431
+ end
432
+
433
+
434
+ loop do
435
+ loop do
436
+ foo = a.find_max(para, eb, opt: opt_a)
437
+ break if sca == foo
438
+ bar = opt_a.irep.para_call(para, *foo)
439
+ break if pa < bar
440
+ sca, pa = foo, bar
441
+ scb = b.search(para, pa, opt: opt_b)
442
+ foo = Mgmg.exp(*scb)
443
+ break if eb == foo
444
+ eb = foo
445
+ end
446
+ ea = Mgmg.exp(*sca)
447
+ while ea==eb do
448
+ res = 0
449
+ begin
450
+ sca = a.find_max(para, ea-1, opt: opt_a)
451
+ rescue Mgmg::SearchCutException
452
+ res += 1
453
+ end
454
+ begin
455
+ scb = b.find_max(para, eb-1, opt: opt_b)
456
+ rescue Mgmg::SearchCutException
457
+ res += 1
458
+ end
347
459
  ea, eb = Mgmg.exp(*sca), Mgmg.exp(*scb)
348
460
  pa, pb = opt_a.irep.para_call(para, *sca), opt_b.irep.para_call(para, *scb)
349
- if ea < eb
350
- ret = tagl
351
- sca = a.search(para, pa + Eighth, opt: opt_a)
352
- tagl = opt_a.irep.para_call(para, *sca)
353
- scb = b.search(para, tagl, opt: opt_b)
354
- elsif ea == eb
355
- if pb < pa
356
- ret = tagl
357
- sca = a.search(para, pa + Eighth, opt: opt_a)
358
- tagl = opt_a.irep.para_call(para, *sca)
359
- scb = b.search(para, tagl, opt: opt_b)
360
- else
361
- scb = b.search(para, pb + Eighth, opt: opt_b)
362
- tagl = opt_b.irep.para_call(para, *scb)
363
- sca = a.search(para, tagl, opt: opt_a)
461
+ if ea < eb || ( ea == eb && pb < pa )
462
+ until pa < pb
463
+ scb = b.search(para, pb+Eighth, opt: opt_b)
464
+ pb = opt_b.irep.para_call(para, *scb)
364
465
  end
365
- else
366
- sca = a.search(para, pa + Eighth, opt: opt_a)
367
- tagl = opt_a.irep.para_call(para, *sca)
368
- scb = b.search(para, tagl, opt: opt_b)
466
+ return [pa, pb]
467
+ elsif res == 2
468
+ raise Mgmg::SearchCutException, "given recipes are never reversed from the start target=#{start.comma3} until #{pa.comma3}"
369
469
  end
370
470
  end
371
- if ret.nil?
372
- tagu = next_tagu
373
- next_sca[-1] -= 2
374
- tagl = opt_a.irep.para_call(para, *next_sca)
375
- if tagl == tagu
376
- tagl = term
471
+ if ea < eb
472
+ pb = opt_b.irep.para_call(para, *scb)
473
+ until pa < pb
474
+ scb = b.search(para, pb+Eighth, opt: opt_b)
475
+ pb = opt_b.irep.para_call(para, *scb)
377
476
  end
378
- else
379
- pa = opt_a.irep.para_call(para, *a.search(para, ret+Eighth, opt: opt_a))
380
- pb = opt_b.irep.para_call(para, *b.search(para, ret+Eighth, opt: opt_b))
381
- return [ret.to_ii, [pa, pb].min]
477
+ return [pa, pb]
382
478
  end
383
479
  end
384
480
  raise UnexpectedError
@@ -386,7 +482,32 @@ module Mgmg
386
482
 
387
483
  module_function def find_lubounds(a, b, para, lower, upper, opt_a: Mgmg::Option.new, opt_b: Mgmg::Option.new)
388
484
  xl, yl = find_lowerbound(a, b, para, lower, upper, opt_a:, opt_b:)
389
- xu, yu = find_upperbound(a, b, para, upper, lower, opt_a:, opt_b:)
485
+ xu, yu = find_upperbound(a, b, para, upper, opt_a:, opt_b:)
390
486
  [xl, yl, xu, yu]
391
487
  end
488
+ module_function def find_lubounds2(a, b, para, lower, upper, opt_a: Mgmg::Option.new, opt_b: Mgmg::Option.new)
489
+ xl, yl, xu, yu = find_lubounds(a, b, para, lower, upper, opt_a: Mgmg::Option.new, opt_b: Mgmg::Option.new)
490
+ if a.kind_of?(Recipe)
491
+ opt_a = a.option.dup
492
+ a = a.recipe
493
+ else
494
+ opt_a = opt_a.dup.set_default(a)
495
+ end
496
+ if b.kind_of?(Recipe)
497
+ opt_b = b.option.dup
498
+ b = b.recipe
499
+ else
500
+ opt_b = opt_b.dup.set_default(b)
501
+ end
502
+ sca, scb = a.search(para, lower, opt: opt_a), b.search(para, lower, opt: opt_b)
503
+ ea, eb = Mgmg.exp(*sca), Mgmg.exp(*scb)
504
+ pa, pb = opt_a.irep.para_call(para, *sca), opt_b.irep.para_call(para, *scb)
505
+ if eb < ea || ( ea == eb && pa < pb )
506
+ a, b, opt_a, opt_b, sca, scb, ea, eb = b, a, opt_b, opt_a, scb, sca, eb, ea
507
+ end
508
+ sca, scb = a.search(para, xl, opt: opt_a), b.search(para, yu, opt: opt_b)
509
+ ea, eb = Mgmg.exp(*sca), Mgmg.exp(*scb)
510
+ pa, pb = opt_a.irep.para_call(para, *sca), opt_b.irep.para_call(para, *scb)
511
+ [sca, ea, pa, scb, eb, pb]
512
+ end
392
513
  end
data/lib/mgmg/utils.rb CHANGED
@@ -158,7 +158,11 @@ module Mgmg
158
158
  end
159
159
  end
160
160
  module_function def invexp2(exp, comp)
161
- ret = Math.sqrt(exp - (2*((comp-1)**2)) - 3).floor + 2
161
+ begin
162
+ ret = Math.sqrt(exp - (2*((comp-1)**2)) - 3).floor + 2
163
+ rescue Math::DomainError
164
+ return -1
165
+ end
162
166
  if Mgmg.exp(ret, comp) <= exp
163
167
  ret
164
168
  else
@@ -166,7 +170,11 @@ module Mgmg
166
170
  end
167
171
  end
168
172
  module_function def invexp2c(exp, s)
169
- ret = Math.sqrt((exp - (((s-1)**2)) - 3).quo(2)).floor + 2
173
+ begin
174
+ ret = Math.sqrt((exp - (((s-1)**2)) - 3).quo(2)).floor + 2
175
+ rescue Math::DomainError
176
+ return -1
177
+ end
170
178
  if Mgmg.exp(s, ret) <= exp
171
179
  ret
172
180
  else
@@ -174,7 +182,12 @@ module Mgmg
174
182
  end
175
183
  end
176
184
  module_function def invexp3(exp, sa, comp)
177
- ret = Math.sqrt(exp - ((sa-1)**2) - (2*((comp-1)**2)) - 4).floor + 2
185
+ return invexp2(exp, comp) if sa < 0
186
+ begin
187
+ ret = Math.sqrt(exp - ((sa-1)**2) - (2*((comp-1)**2)) - 4).floor + 2
188
+ rescue Math::DomainError
189
+ return -1
190
+ end
178
191
  if Mgmg.exp(ret, sa, comp) <= exp
179
192
  ret
180
193
  else
@@ -182,7 +195,16 @@ module Mgmg
182
195
  end
183
196
  end
184
197
  module_function def invexp3c(exp, smith, armor)
185
- ret = Math.sqrt((exp - ((smith-1)**2) - ((armor-1)**2) - 4).quo(2)).floor + 2
198
+ if smith < 0
199
+ return invexp2c(exp, armor)
200
+ elsif armor < 0
201
+ return invexp2c(exp, smith)
202
+ end
203
+ begin
204
+ ret = Math.sqrt((exp - ((smith-1)**2) - ((armor-1)**2) - 4).quo(2)).floor + 2
205
+ rescue Math::DomainError
206
+ return -1
207
+ end
186
208
  if Mgmg.exp(smith, armor, ret) <= exp
187
209
  ret
188
210
  else
data/lib/mgmg/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Mgmg
2
- VERSION = "1.5.7"
2
+ VERSION = "1.6.1"
3
3
  end
data/lib/mgmg.rb CHANGED
@@ -89,6 +89,8 @@ class String
89
89
  pd = self.poly(:phydef, opt:)
90
90
  md = self.poly(:magmag, opt:)
91
91
  pd <= md ? pd : md
92
+ when :hs
93
+ self.poly(:hp, opt:) + self.poly(:str, opt:)
92
94
  when :cost
93
95
  if opt.include_system_equips and Mgmg::SystemEquip.has_key?(self) then
94
96
  return Mgmg::TPolynomial.new(Mgmg::Mat.new(1, 1, 0.quo(1)), 28, 0, 12, 12)
@@ -140,7 +142,13 @@ class String
140
142
  end
141
143
  module Enumerable
142
144
  using Mgmg::Refiner
143
- def to_recipe(para=:power, **kw)
145
+ def to_recipe(para=:power, allow_over20: false, **kw)
146
+ unless allow_over20
147
+ self.each do |e|
148
+ foo = e.build(-1).star
149
+ raise Mgmg::Over20Error, foo if 20 < foo
150
+ end
151
+ end
144
152
  Mgmg::Recipe.new(self, para, **kw)
145
153
  end
146
154
  def build(smith=-1, armor=smith, comp=armor.tap{armor=smith}, opt: Mgmg::Option.new)
data/reference.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # リファレンス
2
2
  本ライブラリで定義される主要なメソッドを以下に解説します.
3
3
 
4
- ## `String#to_recipe(para=:power, allow_over20: false, **kw)`,`Enumerable#to_recipe(para=:power, **kw)`
4
+ ## `String#to_recipe(para=:power, allow_over20: false, **kw)`,`Enumerable#to_recipe(para=:power, allow_over20: false, **kw)`
5
5
  レシピ文字列である`self`と,注目パラメータ`para`,オプション`Mgmg.option(**kw)`をセットにした[後述](#mgmgrecipe)の`Mgmg::Recipe`オブジェクトを生成して返します.デフォルト値としてセットされたパラメータを使用する以外は,レシピ文字列と同様に扱えます.
6
6
 
7
7
  `allow_over20`が偽の場合,レシピの☆を確認し,20を超える場合は,例外`Mgmg::Over20Error`を発生します.このチェックを抑制したい場合は,真にしてください.
@@ -161,7 +161,7 @@
161
161
 
162
162
  `opt_a`,`opt_b`には,それぞれ`a`と`b`に関するオプションパラメータを指定します.`smith_min`,`armor_min`,`min_smith`,`left_associative`,`reinforcement`,`irep`を使用します.
163
163
 
164
- ## `Mgmg.#find_upperbound(a, b, para, start, term, opt_a: Mgmg.option(), opt_b: Mgmg.option())`
164
+ ## `Mgmg.#find_upperbound(a, b, para, start, opt_a: Mgmg.option(), opt_b: Mgmg.option())`
165
165
  `Mgmg.#find_lowerbound`とは逆に,目標値を下げながら,優劣が逆転する最大の目標値を探索し,返します.返り値は`[逆転する最大目標値, 逆転前の最小para値]`です.目標値が,前者よりも少しでも大きいと逆転が起こらず(逆転する目標値の上限),少しだけ大きい時の`para`値が後者になります.
166
166
 
167
167
  `opt_a`,`opt_b`は,`Mgmg.#find_lowerbound`と同様です.
@@ -455,7 +455,7 @@ alias として`*`があるほか`scalar(1.quo(value))`として`quo`,`/`,`s
455
455
  |smith_min|`recipe.min_level(target_weight)`|非対応|鍛冶Lvに関する探索範囲の最小値|`String#search`など|
456
456
  |armor_min|`recipe.min_level(*target_weight)[1]`|非対応|防具製作Lvに関する探索範囲の最小値|`Enumerable#search`など.`String`系では代わりに`smith_min`を使う|
457
457
  |comp_min|`recipe.min_comp`|非対応|道具製作Lvに関する探索範囲の最小値|`String#search`など|
458
- |smith_max, armor_max, comp_max|`10000`|対応|各製作Lvの探索範囲の最大値|`String#search`など|
458
+ |smith_max, armor_max, comp_max|`10,000`|対応|各製作Lvの探索範囲の最大値|`String#search`など|
459
459
  |target_weight|`0`|非対応|`smith_min`のデフォルト値計算に使う目標重量|`String#search`など|
460
460
  |step|`1`|非対応|探索時において道具製作Lvを動かす幅|`String#search`など|
461
461
  |magdef_maximize|`true`|非対応|目標を魔防最大(真)かコスト最小(偽)にするためのスイッチ|`String#phydef_optimize`|
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.7
4
+ version: 1.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - KAZOON
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-10-15 00:00:00.000000000 Z
11
+ date: 2022-10-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -104,7 +104,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
104
104
  - !ruby/object:Gem::Version
105
105
  version: '0'
106
106
  requirements: []
107
- rubygems_version: 3.3.23
107
+ rubygems_version: 3.3.24
108
108
  signing_key:
109
109
  specification_version: 4
110
110
  summary: Calculate specs of equipments of Megurimeguru, a game produced by Kou.