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 +4 -4
- data/CHANGELOG.md +9 -0
- data/lib/mgmg/equip.rb +3 -0
- data/lib/mgmg/ir.rb +3 -0
- data/lib/mgmg/option.rb +1 -1
- data/lib/mgmg/recipe.rb +1 -1
- data/lib/mgmg/search.rb +200 -79
- data/lib/mgmg/utils.rb +26 -4
- data/lib/mgmg/version.rb +1 -1
- data/lib/mgmg.rb +9 -1
- data/reference.md +3 -3
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b9da45e92b9c5fb1cae6577e532cce280c997f46c1df019514b1eb976d89f03f
|
4
|
+
data.tar.gz: 6f905a8d05126dbc81c71e73ddd64988e96a2033f520f6ded8b595ac96eb06be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
data/lib/mgmg/ir.rb
CHANGED
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:
|
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).
|
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
|
219
|
-
opt.
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
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,
|
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
|
-
|
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
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
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
|
-
|
351
|
-
|
352
|
-
|
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
|
-
|
366
|
-
|
367
|
-
|
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
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
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
|
-
|
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,
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
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,
|
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|`
|
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.
|
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-
|
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.
|
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.
|