mgmg 1.5.6 → 1.6.0
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 +10 -2
- data/lib/mgmg/option.rb +1 -1
- data/lib/mgmg/recipe.rb +4 -0
- data/lib/mgmg/search.rb +229 -83
- data/lib/mgmg/utils.rb +50 -2
- data/lib/mgmg/version.rb +1 -1
- data/reference.md +13 -4
- 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: beb7f465bd84969abfb7ce5897d95f46171e84c2a0e3de9a14c42ccef5c23dfc
|
4
|
+
data.tar.gz: '09a7eda9204cb28a7a9fc24857353fb5cdcfd21e44564d4a25997658c92fdfd3'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5642dc8af3ac998c76650047c97747a32f2b631e9f6438c6a95e6068a9534dcd726d34dd8466fb1cba0e787f11847552aab4812e1b32d4320ff23a5ec8e15e18
|
7
|
+
data.tar.gz: a327745f26724f3f6bbeabf2ed9fcb687ca4e462ac608c219165845b6ccfeafd5a30c32fdda4ee182dd268db8e0b4528e0acc1bc819eaaae0698d9400c764797
|
data/CHANGELOG.md
CHANGED
@@ -158,6 +158,14 @@
|
|
158
158
|
- `String#min_level`において,正しい答えを返さなくなったバグを修正.
|
159
159
|
|
160
160
|
## 1.5.6 2022/07/17
|
161
|
-
- `
|
161
|
+
- `Mgmg.#find_lowerbound`,`Mgmg.#find_upperbound`における探索刻み幅を1から1/8に変更し,コーナーケースでの精度を上昇させた.
|
162
162
|
- `Enumerable#min_level`が,原料☆によるレベル制限を無視した値を返すことがあったバグを修正.
|
163
|
-
- `
|
163
|
+
- `Mgmg.#find_lowerbound`や`Mgmg::IR#atk_sd` などにおいて,返り値が,分母が1の`Rational`である場合,代わりに`Integer`を返すように修正.
|
164
|
+
|
165
|
+
## 1.5.7 2022/10/15
|
166
|
+
- 経験値上限を指定して,目標パラメータを最大化する製作Lvを返す`Mgmg::Recipe#find_max`を追加.
|
167
|
+
- `Mgmg::Recipe#search`において,解の経験値が`cut_exp`ちょうどの場合に例外となっていたバグを修正.
|
168
|
+
|
169
|
+
## 1.6.0 2022/10/18
|
170
|
+
- `Mgmg.#find_upperbound`のアルゴリズムを改善し,探索下限目標値の引数を削除.
|
171
|
+
- `Enumerable#search`,`Enumerable#find_max`が正しい解を返さない場合があったバグを修正.
|
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
@@ -93,6 +93,10 @@ module Mgmg
|
|
93
93
|
opt = temp_opt(**kw)
|
94
94
|
@recipe.search(para, target, opt:)
|
95
95
|
end
|
96
|
+
def find_max(max_exp, para: @para, **kw)
|
97
|
+
opt = temp_opt(**kw)
|
98
|
+
@recipe.find_max(para, max_exp, opt:)
|
99
|
+
end
|
96
100
|
private def correct_level(s, ac, x, opt)
|
97
101
|
if s.nil?
|
98
102
|
if x.equal?(false)
|
data/lib/mgmg/search.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
using Mgmg::Refiner
|
1
2
|
class String
|
2
3
|
def smith_search(para, target, comp, opt: Mgmg::Option.new)
|
3
4
|
opt = opt.dup.set_default(self)
|
@@ -55,9 +56,9 @@ class String
|
|
55
56
|
opt.comp_max = comp_search(para, target, opt.smith_min, opt:)
|
56
57
|
ret = nil
|
57
58
|
exp = Mgmg.exp(opt.smith_min, opt.comp_max)
|
58
|
-
opt.cut_exp, ret = exp, [opt.smith_min, opt.comp_max] if exp
|
59
|
+
opt.cut_exp, ret = exp, [opt.smith_min, opt.comp_max] if exp <= opt.cut_exp
|
59
60
|
exp = Mgmg.exp(opt.smith_max, opt.comp_min)
|
60
|
-
opt.cut_exp, ret = exp, [opt.smith_max, opt.comp_min] if exp < opt.cut_exp
|
61
|
+
opt.cut_exp, ret = exp, [opt.smith_max, opt.comp_min] if ( exp < opt.cut_exp || (ret.nil? && exp==opt.cut_exp) )
|
61
62
|
(opt.comp_min+opt.step).step(opt.comp_max-1, opt.step) do |comp|
|
62
63
|
break if opt.cut_exp < Mgmg.exp(opt.smith_min, comp)
|
63
64
|
smith = smith_search(para, target, comp, opt:)
|
@@ -71,7 +72,36 @@ class String
|
|
71
72
|
end
|
72
73
|
rescue Mgmg::SearchCutException
|
73
74
|
end
|
74
|
-
|
75
|
+
if ret.nil?
|
76
|
+
max = opt.irep.para_call(para, *find_max(para, opt.cut_exp, opt:))
|
77
|
+
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}"
|
78
|
+
end
|
79
|
+
ret
|
80
|
+
end
|
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
|
93
|
+
def find_max(para, max_exp, opt: Mgmg::Option.new)
|
94
|
+
opt = opt.dup.set_default(self)
|
95
|
+
exp = Mgmg.exp(opt.smith_min, opt.comp_min)
|
96
|
+
raise Mgmg::SearchCutException, "the recipe requires #{exp.comma3} experiment points, which exceeds given max_exp=#{max_exp.comma3}" if max_exp < exp
|
97
|
+
ret = [Mgmg.invexp2(max_exp, opt.comp_min), opt.comp_min]
|
98
|
+
max = opt.irep.para_call(para, *ret)
|
99
|
+
(opt.comp_min+1).upto(Mgmg.invexp2c(max_exp, opt.smith_min)) do |comp|
|
100
|
+
smith = Mgmg.invexp2(max_exp, comp)
|
101
|
+
cur = opt.irep.para_call(para, smith, comp)
|
102
|
+
smith = minimize_smith(para, smith, comp, cur, opt) if max <= cur
|
103
|
+
ret, max = [smith, comp], cur if ( max < cur || ( max == cur && Mgmg.exp(smith, comp) < Mgmg.exp(*ret) ) )
|
104
|
+
end
|
75
105
|
ret
|
76
106
|
end
|
77
107
|
end
|
@@ -138,10 +168,10 @@ module Enumerable
|
|
138
168
|
opt.armor_max = armor_search(para, target, opt.smith_min, comp, opt: opt_nocut)
|
139
169
|
ret = nil
|
140
170
|
exp = Mgmg.exp(opt.smith_min, opt.armor_max, comp)
|
141
|
-
opt.cut_exp, ret = exp, [opt.smith_min, opt.armor_max] if exp
|
171
|
+
opt.cut_exp, ret = exp, [opt.smith_min, opt.armor_max] if exp <= opt.cut_exp
|
142
172
|
exp2 = Mgmg.exp(opt.smith_max, opt.armor_min, comp)
|
143
173
|
if exp2 < exp
|
144
|
-
opt.cut_exp, ret = exp2, [opt.smith_max, opt.armor_min] if exp2
|
174
|
+
opt.cut_exp, ret = exp2, [opt.smith_max, opt.armor_min] if exp2 <= opt.cut_exp
|
145
175
|
(opt.armor_min+1).upto(opt.armor_max-1) do |armor|
|
146
176
|
break if opt.cut_exp < Mgmg.exp(opt.smith_min, armor, comp)
|
147
177
|
smith = smith_search(para, target, armor, comp, opt:)
|
@@ -193,19 +223,79 @@ module Enumerable
|
|
193
223
|
end
|
194
224
|
opt.comp_max
|
195
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
|
196
286
|
def search(para, target, opt: Mgmg::Option.new)
|
197
287
|
opt = opt.dup.set_default(self)
|
198
288
|
opt.comp_min = comp_search(para, target, opt.smith_max, opt.armor_max, opt:)
|
199
|
-
opt.
|
200
|
-
opt.
|
289
|
+
opt_nocut = opt.dup; opt_nocut.cut_exp = Float::INFINITY
|
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)
|
201
294
|
raise Mgmg::SearchCutException if opt.cut_exp < Mgmg.exp(opt.smith_min, opt.armor_min, opt.comp_min)
|
202
295
|
opt.comp_max = comp_search(para, target, opt.smith_min, opt.armor_min, opt:)
|
203
|
-
ret = nil
|
204
296
|
exp = Mgmg.exp(opt.smith_min, opt.armor_min, opt.comp_max)
|
205
|
-
opt.cut_exp, ret = exp, [opt.smith_min, opt.armor_min,opt. comp_max] if exp
|
206
|
-
|
207
|
-
opt.cut_exp, ret = exp, [opt.smith_max, opt.armor_max, opt.comp_min] if exp < opt.cut_exp
|
208
|
-
(opt.comp_min+1).upto(opt.comp_max-1) do |comp|
|
297
|
+
opt.cut_exp, ret = exp, [opt.smith_min, opt.armor_min,opt. comp_max] if exp <= opt.cut_exp
|
298
|
+
(opt.comp_min).upto(opt.comp_max-1) do |comp|
|
209
299
|
break if opt.cut_exp < Mgmg.exp(opt.smith_min, opt.armor_min, comp)
|
210
300
|
smith, armor = sa_search(para, target, comp, opt:)
|
211
301
|
exp = Mgmg.exp(smith, armor, comp)
|
@@ -218,14 +308,46 @@ module Enumerable
|
|
218
308
|
end
|
219
309
|
rescue Mgmg::SearchCutException
|
220
310
|
end
|
221
|
-
|
311
|
+
if ret.nil?
|
312
|
+
max = opt.irep.para_call(para, *find_max(para, opt.cut_exp, opt:))
|
313
|
+
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}"
|
314
|
+
end
|
315
|
+
ret
|
316
|
+
end
|
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
|
330
|
+
def find_max(para, max_exp, opt: Mgmg::Option.new)
|
331
|
+
opt = opt.dup.set_default(self)
|
332
|
+
exp = Mgmg.exp(opt.smith_min, opt.armor_min, opt.comp_min)
|
333
|
+
raise Mgmg::SearchCutException, "the recipe requires #{exp.comma3} experiment points, which exceeds given max_exp=#{max_exp.comma3}" if max_exp < exp
|
334
|
+
ret = [Mgmg.invexp3(max_exp, opt.armor_min, opt.comp_min), opt.armor_min, opt.comp_min]
|
335
|
+
max = opt.irep.para_call(para, *ret)
|
336
|
+
(opt.comp_min).step(Mgmg.invexp3c(max_exp, opt.smith_min, opt.armor_min), opt.step) do |comp|
|
337
|
+
opt.armor_min.upto(Mgmg.invexp3(max_exp, opt.smith_min, comp)) do |armor|
|
338
|
+
smith = Mgmg.invexp3(max_exp, armor, comp)
|
339
|
+
cur = opt.irep.para_call(para, smith, armor, comp)
|
340
|
+
smith = minimize_smith(para, smith, armor, comp, cur, opt) if max <= cur
|
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
|
343
|
+
end
|
344
|
+
end
|
222
345
|
ret
|
223
346
|
end
|
224
347
|
end
|
225
348
|
|
226
349
|
module Mgmg
|
227
350
|
Eighth = 1.quo(8)
|
228
|
-
using Refiner
|
229
351
|
module_function def find_lowerbound(a, b, para, start, term, opt_a: Option.new, opt_b: Option.new)
|
230
352
|
if term <= start
|
231
353
|
raise ArgumentError, "start < term is needed, (start, term) = (#{start}, #{term}) are given"
|
@@ -244,38 +366,42 @@ module Mgmg
|
|
244
366
|
end
|
245
367
|
sca, scb = a.search(para, start, opt: opt_a), b.search(para, start, opt: opt_b)
|
246
368
|
ea, eb = Mgmg.exp(*sca), Mgmg.exp(*scb)
|
247
|
-
|
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 )
|
248
371
|
a, b, opt_a, opt_b, sca, scb, ea, eb = b, a, opt_b, opt_a, scb, sca, eb, ea
|
249
372
|
end
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
if pa < pb
|
264
|
-
return [(tag-Eighth).to_ii, pa]
|
265
|
-
else
|
266
|
-
tag = pb + Eighth
|
267
|
-
end
|
268
|
-
else
|
373
|
+
|
374
|
+
loop do
|
375
|
+
loop do
|
376
|
+
foo = a.find_max(para, eb, opt: opt_a)
|
377
|
+
break if sca == foo
|
378
|
+
sca, pa = foo, opt_a.irep.para_call(para, *foo)
|
379
|
+
scb = b.search(para, pa, opt: opt_b)
|
380
|
+
foo = Mgmg.exp(*scb)
|
381
|
+
break if eb == foo
|
382
|
+
eb = foo
|
383
|
+
end
|
384
|
+
ea = Mgmg.exp(*sca)
|
385
|
+
while ea<=eb
|
269
386
|
tag = pa + Eighth
|
387
|
+
raise Mgmg::SearchCutException, "given recipes are never reversed from start target=#{start.comma3} until term target=#{term.comma3}" if term < tag
|
388
|
+
sca, scb = a.search(para, tag, opt: opt_a), b.search(para, tag, opt: opt_b)
|
389
|
+
ea, eb = Mgmg.exp(*sca), Mgmg.exp(*scb)
|
390
|
+
pa, pb = opt_a.irep.para_call(para, *sca), opt_b.irep.para_call(para, *scb)
|
391
|
+
break if ea == eb && pa < pb
|
392
|
+
end
|
393
|
+
if eb < ea || ( ea == eb && pa < pb )
|
394
|
+
until ea < eb || ( ea == eb && pb < pa )
|
395
|
+
sca = a.find_max(para, ea-1, opt: opt_a)
|
396
|
+
ea, pa = Mgmg.exp(*sca), opt_a.irep.para_call(para, *sca)
|
397
|
+
end
|
398
|
+
return [pa, pb]
|
270
399
|
end
|
271
400
|
end
|
272
401
|
raise UnexpectedError
|
273
402
|
end
|
274
403
|
|
275
|
-
module_function def find_upperbound(a, b, para, start,
|
276
|
-
if start <= term
|
277
|
-
raise ArgumentError, "term < start is needed, (start, term) = (#{start}, #{term}) are given"
|
278
|
-
end
|
404
|
+
module_function def find_upperbound(a, b, para, start, opt_a: Option.new, opt_b: Option.new)
|
279
405
|
if a.kind_of?(Recipe)
|
280
406
|
opt_a = a.option.dup
|
281
407
|
a = a.recipe
|
@@ -290,58 +416,53 @@ module Mgmg
|
|
290
416
|
end
|
291
417
|
sca, scb = a.search(para, start, opt: opt_a), b.search(para, start, opt: opt_b)
|
292
418
|
ea, eb = Mgmg.exp(*sca), Mgmg.exp(*scb)
|
293
|
-
|
419
|
+
pa, pb = opt_a.irep.para_call(para, *sca), opt_b.irep.para_call(para, *scb)
|
420
|
+
if ea < eb || ( ea == eb && pb < pa )
|
294
421
|
a, b, opt_a, opt_b, sca, scb, ea, eb = b, a, opt_b, opt_a, scb, sca, eb, ea
|
295
422
|
end
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
423
|
+
|
424
|
+
loop do
|
425
|
+
loop do
|
426
|
+
foo = a.find_max(para, eb, opt: opt_a)
|
427
|
+
break if sca == foo
|
428
|
+
sca, pa = foo, opt_a.irep.para_call(para, *foo)
|
429
|
+
scb = b.search(para, pa, opt: opt_b)
|
430
|
+
foo = Mgmg.exp(*scb)
|
431
|
+
break if eb == foo
|
432
|
+
eb = foo
|
433
|
+
end
|
434
|
+
ea = Mgmg.exp(*sca)
|
435
|
+
while ea==eb do
|
436
|
+
res = 0
|
437
|
+
begin
|
438
|
+
sca = a.find_max(para, ea-1, opt: opt_a)
|
439
|
+
rescue Mgmg::SearchCutException
|
440
|
+
res += 1
|
441
|
+
end
|
442
|
+
begin
|
443
|
+
scb = b.find_max(para, eb-1, opt: opt_b)
|
444
|
+
rescue Mgmg::SearchCutException
|
445
|
+
res += 1
|
446
|
+
end
|
310
447
|
ea, eb = Mgmg.exp(*sca), Mgmg.exp(*scb)
|
311
448
|
pa, pb = opt_a.irep.para_call(para, *sca), opt_b.irep.para_call(para, *scb)
|
312
|
-
if ea < eb
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
scb = b.search(para, tagl, opt: opt_b)
|
317
|
-
elsif ea == eb
|
318
|
-
if pb < pa
|
319
|
-
ret = tagl
|
320
|
-
sca = a.search(para, pa + Eighth, opt: opt_a)
|
321
|
-
tagl = opt_a.irep.para_call(para, *sca)
|
322
|
-
scb = b.search(para, tagl, opt: opt_b)
|
323
|
-
else
|
324
|
-
scb = b.search(para, pb + Eighth, opt: opt_b)
|
325
|
-
tagl = opt_b.irep.para_call(para, *scb)
|
326
|
-
sca = a.search(para, tagl, opt: opt_a)
|
449
|
+
if ea < eb || ( ea == eb && pb < pa )
|
450
|
+
until pa < pb
|
451
|
+
scb = b.search(para, pb+Eighth, opt: opt_b)
|
452
|
+
pb = opt_b.irep.para_call(para, *scb)
|
327
453
|
end
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
scb = b.search(para, tagl, opt: opt_b)
|
454
|
+
return [pa, pb]
|
455
|
+
elsif res == 2
|
456
|
+
raise Mgmg::SearchCutException, "given recipes are never reversed from the start target=#{start.comma3} until #{pa.comma3}"
|
332
457
|
end
|
333
458
|
end
|
334
|
-
if
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
tagl = term
|
459
|
+
if ea < eb
|
460
|
+
pb = opt_b.irep.para_call(para, *scb)
|
461
|
+
until pa < pb
|
462
|
+
scb = b.search(para, pb+Eighth, opt: opt_b)
|
463
|
+
pb = opt_b.irep.para_call(para, *scb)
|
340
464
|
end
|
341
|
-
|
342
|
-
pa = opt_a.irep.para_call(para, *a.search(para, ret+Eighth, opt: opt_a))
|
343
|
-
pb = opt_b.irep.para_call(para, *b.search(para, ret+Eighth, opt: opt_b))
|
344
|
-
return [ret.to_ii, [pa, pb].min]
|
465
|
+
return [pa, pb]
|
345
466
|
end
|
346
467
|
end
|
347
468
|
raise UnexpectedError
|
@@ -349,7 +470,32 @@ module Mgmg
|
|
349
470
|
|
350
471
|
module_function def find_lubounds(a, b, para, lower, upper, opt_a: Mgmg::Option.new, opt_b: Mgmg::Option.new)
|
351
472
|
xl, yl = find_lowerbound(a, b, para, lower, upper, opt_a:, opt_b:)
|
352
|
-
xu, yu = find_upperbound(a, b, para, upper,
|
473
|
+
xu, yu = find_upperbound(a, b, para, upper, opt_a:, opt_b:)
|
353
474
|
[xl, yl, xu, yu]
|
354
475
|
end
|
476
|
+
module_function def find_lubounds2(a, b, para, lower, upper, opt_a: Mgmg::Option.new, opt_b: Mgmg::Option.new)
|
477
|
+
xl, yl, xu, yu = find_lubounds(a, b, para, lower, upper, opt_a: Mgmg::Option.new, opt_b: Mgmg::Option.new)
|
478
|
+
if a.kind_of?(Recipe)
|
479
|
+
opt_a = a.option.dup
|
480
|
+
a = a.recipe
|
481
|
+
else
|
482
|
+
opt_a = opt_a.dup.set_default(a)
|
483
|
+
end
|
484
|
+
if b.kind_of?(Recipe)
|
485
|
+
opt_b = b.option.dup
|
486
|
+
b = b.recipe
|
487
|
+
else
|
488
|
+
opt_b = opt_b.dup.set_default(b)
|
489
|
+
end
|
490
|
+
sca, scb = a.search(para, lower, opt: opt_a), b.search(para, lower, opt: opt_b)
|
491
|
+
ea, eb = Mgmg.exp(*sca), Mgmg.exp(*scb)
|
492
|
+
pa, pb = opt_a.irep.para_call(para, *sca), opt_b.irep.para_call(para, *scb)
|
493
|
+
if eb < ea || ( ea == eb && pa < pb )
|
494
|
+
a, b, opt_a, opt_b, sca, scb, ea, eb = b, a, opt_b, opt_a, scb, sca, eb, ea
|
495
|
+
end
|
496
|
+
sca, scb = a.search(para, xl, opt: opt_a), b.search(para, yu, opt: opt_b)
|
497
|
+
ea, eb = Mgmg.exp(*sca), Mgmg.exp(*scb)
|
498
|
+
pa, pb = opt_a.irep.para_call(para, *sca), opt_b.irep.para_call(para, *scb)
|
499
|
+
[sca, ea, pa, scb, eb, pb]
|
500
|
+
end
|
355
501
|
end
|
data/lib/mgmg/utils.rb
CHANGED
@@ -158,10 +158,58 @@ 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
|
166
|
+
if Mgmg.exp(ret, comp) <= exp
|
167
|
+
ret
|
168
|
+
else
|
169
|
+
ret-1
|
170
|
+
end
|
171
|
+
end
|
172
|
+
module_function def invexp2c(exp, s)
|
173
|
+
begin
|
174
|
+
ret = Math.sqrt((exp - (((s-1)**2)) - 3).quo(2)).floor + 2
|
175
|
+
rescue Math::DomainError
|
176
|
+
return -1
|
177
|
+
end
|
178
|
+
if Mgmg.exp(s, ret) <= exp
|
179
|
+
ret
|
180
|
+
else
|
181
|
+
ret-1
|
182
|
+
end
|
162
183
|
end
|
163
184
|
module_function def invexp3(exp, sa, comp)
|
164
|
-
|
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
|
191
|
+
if Mgmg.exp(ret, sa, comp) <= exp
|
192
|
+
ret
|
193
|
+
else
|
194
|
+
ret-1
|
195
|
+
end
|
196
|
+
end
|
197
|
+
module_function def invexp3c(exp, smith, armor)
|
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
|
208
|
+
if Mgmg.exp(smith, armor, ret) <= exp
|
209
|
+
ret
|
210
|
+
else
|
211
|
+
ret-1
|
212
|
+
end
|
165
213
|
end
|
166
214
|
module_function def clear_cache
|
167
215
|
CacheMLS.clear; Equip::Cache.clear; Equip::CacheML.clear; TPolynomial::Cache.clear; IR::Cache.clear
|
data/lib/mgmg/version.rb
CHANGED
data/reference.md
CHANGED
@@ -130,7 +130,7 @@
|
|
130
130
|
`opt`は,`comp_min`,`comp_max`,`left_associative`,`reinforcement`,`irep`を使用します.
|
131
131
|
|
132
132
|
## `String#search(para, target, opt: Mgmg.option())`
|
133
|
-
`c_min=comp_search(para, target, opt.smith_max, opt: opt)` から `c_max=comp_search(para, target, opt.smith_min, opt: opt)` まで,`opt.step`ずつ動かして,
|
133
|
+
道具製作Lvを `c_min=comp_search(para, target, opt.smith_max, opt: opt)` から `c_max=comp_search(para, target, opt.smith_min, opt: opt)` まで,`opt.step`ずつ動かして,
|
134
134
|
`smith_search`を行い,その過程で得られた最小経験値の鍛冶・防具製作Lvと道具製作Lvからなる配列を返します.
|
135
135
|
レシピ中の,対象パラメータの種別値がすべて奇数,または全て偶数であるなら,`opt.step`を`2`にしても探索すべき範囲を網羅できます.
|
136
136
|
その他は`String#smith_seach`と同様です.
|
@@ -139,12 +139,18 @@
|
|
139
139
|
|
140
140
|
## `Enumerable#search(para, target, opt: Mgmg.option())`
|
141
141
|
複数装備の組について,`para`の値が`target`以上となる最小経験値の`[鍛冶Lv,防具製作Lv,道具製作Lv]`を返します.
|
142
|
-
武器のみなら防具製作Lv
|
142
|
+
武器のみなら防具製作Lvは`-1`,防具のみなら鍛冶Lvは`-1`,合成なしなら道具製作Lvは`0`となります.
|
143
143
|
|
144
144
|
`opt.smith_min`および`opt.armor_min`のデフォルト値は,武器・防具の各総重量を`opt.target_weight`で製作するのに必要な鍛冶・防具製作Lv (`self.min_level(*opt.target_weight)`)です.`opt.target_weight`には2要素からなる配列を指定しますが,整数が与えられた場合,それを2つ並べた配列と同等のものとして扱います.合計重量を指定することにはならないので注意してください.`opt.target_weight`のデフォルト値は`0`です.
|
145
145
|
|
146
146
|
その他は,`String#smith_seach`と同様です.
|
147
147
|
|
148
|
+
## `String#find_max(para, max_exp, opt: Mgmg.option())`
|
149
|
+
経験値の合計が`max_exp`以下の範囲で,`para`の値が最大となる鍛冶・防具製作Lvと道具製作Lvからなる配列を返します.`para`の値が最大となる組が複数存在する場合,経験値が小さい方が優先されます.
|
150
|
+
|
151
|
+
## `Enumerable#find_max(para, max_exp, opt: Mgmg.option())`
|
152
|
+
複数装備の組について,経験値の合計が`max_exp`以下の範囲で,`para`の値が最大となる鍛冶Lv,防具製作Lv,道具製作Lvからなる配列を返します.`para`の値が最大となる組が複数存在する場合,経験値が小さい方が優先されます.
|
153
|
+
|
148
154
|
## `Mgmg.#find_lowerbound(a, b, para, start, term, opt_a: Mgmg.option(), opt_b: Mgmg.option())`
|
149
155
|
レシピ`a`とレシピ`b`について,`para`の値を目標値以上にする最小経験値の組において,目標値`start`における優劣が逆転する目標値の下限を探索し,返します.
|
150
156
|
返り値は`[逆転しない最大目標値, 逆転時の最小para値]`です.前者は逆転目標値の下限,後者は,目標値が前者よりも少しだけ大きいときの`para`値です.
|
@@ -155,7 +161,7 @@
|
|
155
161
|
|
156
162
|
`opt_a`,`opt_b`には,それぞれ`a`と`b`に関するオプションパラメータを指定します.`smith_min`,`armor_min`,`min_smith`,`left_associative`,`reinforcement`,`irep`を使用します.
|
157
163
|
|
158
|
-
## `Mgmg.#find_upperbound(a, b, para, start,
|
164
|
+
## `Mgmg.#find_upperbound(a, b, para, start, opt_a: Mgmg.option(), opt_b: Mgmg.option())`
|
159
165
|
`Mgmg.#find_lowerbound`とは逆に,目標値を下げながら,優劣が逆転する最大の目標値を探索し,返します.返り値は`[逆転する最大目標値, 逆転前の最小para値]`です.目標値が,前者よりも少しでも大きいと逆転が起こらず(逆転する目標値の上限),少しだけ大きい時の`para`値が後者になります.
|
160
166
|
|
161
167
|
`opt_a`,`opt_b`は,`Mgmg.#find_lowerbound`と同様です.
|
@@ -449,7 +455,7 @@ alias として`*`があるほか`scalar(1.quo(value))`として`quo`,`/`,`s
|
|
449
455
|
|smith_min|`recipe.min_level(target_weight)`|非対応|鍛冶Lvに関する探索範囲の最小値|`String#search`など|
|
450
456
|
|armor_min|`recipe.min_level(*target_weight)[1]`|非対応|防具製作Lvに関する探索範囲の最小値|`Enumerable#search`など.`String`系では代わりに`smith_min`を使う|
|
451
457
|
|comp_min|`recipe.min_comp`|非対応|道具製作Lvに関する探索範囲の最小値|`String#search`など|
|
452
|
-
|smith_max, armor_max, comp_max|`
|
458
|
+
|smith_max, armor_max, comp_max|`10,000`|対応|各製作Lvの探索範囲の最大値|`String#search`など|
|
453
459
|
|target_weight|`0`|非対応|`smith_min`のデフォルト値計算に使う目標重量|`String#search`など|
|
454
460
|
|step|`1`|非対応|探索時において道具製作Lvを動かす幅|`String#search`など|
|
455
461
|
|magdef_maximize|`true`|非対応|目標を魔防最大(真)かコスト最小(偽)にするためのスイッチ|`String#phydef_optimize`|
|
@@ -482,6 +488,9 @@ alias として`*`があるほか`scalar(1.quo(value))`として`quo`,`/`,`s
|
|
482
488
|
## `Mgmg::Recipe#search(target, para: self.para, **kw)`
|
483
489
|
`self.recipe.search`を呼び出して返します.注目パラメータはセットされたものを自動的に渡しますが,別のパラメータを使いたい場合,キーワード引数で指定します.その他のキーワード引数を与えた場合,オプションのうち,与えられたパラメータのみ一時的に上書きします.
|
484
490
|
|
491
|
+
## `Mgmg::Recipe#find_max(max_exp, para: self.para, **kw)`
|
492
|
+
`self.recipe.find_max`を呼び出して返します.注目パラメータはセットされたものを自動的に渡しますが,別のパラメータを使いたい場合,キーワード引数で指定します.その他のキーワード引数を与えた場合,オプションのうち,与えられたパラメータのみ一時的に上書きします.
|
493
|
+
|
485
494
|
## `Mgmg::Recipe#min_level(w=self.option.target_weight, include_outsourcing=false)`
|
486
495
|
`self.recipe.min_level(w, include_outsourcing)`を返します.`w`のデフォルトパラメータが`target_weight`になっている以外は`String#min_level`または`Enumerable#min_level`と同じです.
|
487
496
|
|
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.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- KAZOON
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-10-18 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.
|