mgmg 1.5.7 → 1.6.1

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: 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.