mgmg 1.2.3 → 1.3.2
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 +28 -1
- data/README.md +117 -23
- data/lib/mgmg.rb +81 -145
- data/lib/mgmg/const.rb +0 -995
- data/lib/mgmg/equip.rb +396 -0
- data/lib/mgmg/optimize.rb +213 -0
- data/lib/mgmg/poly.rb +347 -0
- data/lib/mgmg/search.rb +254 -0
- data/lib/mgmg/system_equip.rb +326 -0
- data/lib/mgmg/utils.rb +315 -0
- data/lib/mgmg/version.rb +1 -1
- data/mgmg.gemspec +3 -3
- metadata +24 -18
data/lib/mgmg/poly.rb
ADDED
@@ -0,0 +1,347 @@
|
|
1
|
+
module Mgmg
|
2
|
+
using Refiner
|
3
|
+
class TPolynomial
|
4
|
+
def initialize(mat, kind, star, main_m, sub_m)
|
5
|
+
@mat, @kind, @star, @main, @sub = mat, kind, star, main_m, sub_m
|
6
|
+
end
|
7
|
+
attr_accessor :mat, :kind, :star, :main, :sub
|
8
|
+
def initialize_copy(obj)
|
9
|
+
@mat, @kind, @star, @main, @sub = obj.mat.dup, obj.kind, obj.star, obj.main, obj.sub
|
10
|
+
end
|
11
|
+
def evaluate(smith, comp=smith)
|
12
|
+
@mat.map_with_index do |e, i, j|
|
13
|
+
e * (smith**i) * (comp**j)
|
14
|
+
end.sum
|
15
|
+
end
|
16
|
+
def to_s(fmt=nil)
|
17
|
+
foo = []
|
18
|
+
(@mat.col_size-1).downto(0) do |c|
|
19
|
+
bar = []
|
20
|
+
(@mat.row_size-1).downto(0) do |s|
|
21
|
+
value = @mat.body[s][c]
|
22
|
+
baz = str(value, fmt)
|
23
|
+
case s
|
24
|
+
when 0
|
25
|
+
# nothing to do
|
26
|
+
when 1
|
27
|
+
baz << 'S'
|
28
|
+
else
|
29
|
+
baz << "S^#{s}"
|
30
|
+
end
|
31
|
+
bar << baz if value != 0
|
32
|
+
end
|
33
|
+
case bar.length
|
34
|
+
when 0
|
35
|
+
next
|
36
|
+
when 1
|
37
|
+
bar = bar[0]
|
38
|
+
else
|
39
|
+
bar = "(#{bar.join('+')})"
|
40
|
+
end
|
41
|
+
case c
|
42
|
+
when 0
|
43
|
+
# nothing to do
|
44
|
+
when 1
|
45
|
+
bar << 'C'
|
46
|
+
else
|
47
|
+
bar << "C^#{c}"
|
48
|
+
end
|
49
|
+
foo << bar
|
50
|
+
end
|
51
|
+
foo.join('+').tap{|r| break str(0.quo(1), fmt) if r==''}
|
52
|
+
end
|
53
|
+
private def str(value, fmt)
|
54
|
+
ret = case fmt
|
55
|
+
when NilClass
|
56
|
+
value.to_s
|
57
|
+
when String
|
58
|
+
fmt % value
|
59
|
+
when Symbol
|
60
|
+
value.__send__(fmt)
|
61
|
+
when Proc
|
62
|
+
fmt.call(value)
|
63
|
+
else
|
64
|
+
raise
|
65
|
+
end
|
66
|
+
if ret[0] == '-' || ( /\//.match(ret) && ret[0] != '(' )
|
67
|
+
"(#{ret})"
|
68
|
+
else
|
69
|
+
ret
|
70
|
+
end
|
71
|
+
end
|
72
|
+
def inspect(fmt=->(r){"Rational(#{r.numerator}, #{r.denominator})"})
|
73
|
+
foo = []
|
74
|
+
(@mat.col_size-1).downto(0) do |c|
|
75
|
+
bar = []
|
76
|
+
(@mat.row_size-1).downto(0) do |s|
|
77
|
+
value = @mat.body[s][c]
|
78
|
+
bar << str(value, fmt)
|
79
|
+
end
|
80
|
+
buff = bar[0]
|
81
|
+
buff = "#{buff}*s+#{bar[1]}" if 1 < bar.length
|
82
|
+
2.upto(bar.length-1) do |i|
|
83
|
+
buff = "(#{buff})*s+#{bar[i]}"
|
84
|
+
end
|
85
|
+
foo << buff
|
86
|
+
end
|
87
|
+
ret = foo[0]
|
88
|
+
1.upto(foo.length-1) do |i|
|
89
|
+
ret = "(#{ret})*c+#{foo[i]}"
|
90
|
+
end
|
91
|
+
ret
|
92
|
+
end
|
93
|
+
def leading(fmt=nil)
|
94
|
+
value = self[-1, -1]
|
95
|
+
if fmt.nil?
|
96
|
+
value
|
97
|
+
else
|
98
|
+
str(value, fmt)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
def smith_balance(other, order=-1)
|
102
|
+
o_org = order
|
103
|
+
order += @mat.col_size if order < 0
|
104
|
+
if order < 0 || @mat.col_size <= order || other.mat.col_size <= order then
|
105
|
+
raise ArgumentError, "given order #{o_org} is out of range [-max(#{@mat.col_size}, #{other.mat.col_size}), max(#{@mat.col_size}, #{other.mat.col_size})-1]"
|
106
|
+
end
|
107
|
+
a, b, c, d = @mat.body[1][order], @mat.body[0][order], other.mat.body[1][order], other.mat.body[0][order]
|
108
|
+
if a == c
|
109
|
+
return( b == d )
|
110
|
+
else
|
111
|
+
return( (d-b).quo(a-c) )
|
112
|
+
end
|
113
|
+
end
|
114
|
+
def smith_fix(smith, fmt=nil)
|
115
|
+
foo = []
|
116
|
+
(@mat.col_size-1).downto(0) do |c|
|
117
|
+
bar = 0
|
118
|
+
(@mat.row_size-1).downto(0) do |s|
|
119
|
+
bar += ( @mat.body[s][c] * (smith**s) )
|
120
|
+
end
|
121
|
+
bar = str(bar, fmt)
|
122
|
+
case c
|
123
|
+
when 0
|
124
|
+
# nothing to do
|
125
|
+
when 1
|
126
|
+
bar << 'C'
|
127
|
+
else
|
128
|
+
bar << "C^#{c}"
|
129
|
+
end
|
130
|
+
foo << bar
|
131
|
+
end
|
132
|
+
foo.join('+')
|
133
|
+
end
|
134
|
+
|
135
|
+
alias :+@ :dup
|
136
|
+
def -@
|
137
|
+
ret = self.dup
|
138
|
+
ret.mat.scalar!(-1)
|
139
|
+
ret
|
140
|
+
end
|
141
|
+
def +(other)
|
142
|
+
other = self.coerce(other)[0] unless other.kind_of?(self.class)
|
143
|
+
mat = @mat.padd(other.mat)
|
144
|
+
self.class.new(mat, 28, 0, 12, 12)
|
145
|
+
end
|
146
|
+
def -(other)
|
147
|
+
other = self.coerce(other)[0] unless other.kind_of?(self.class)
|
148
|
+
mat = @mat.padd(other.mat.scalar(-1))
|
149
|
+
self.class.new(mat, 28, 0, 12, 12)
|
150
|
+
end
|
151
|
+
def scalar(val)
|
152
|
+
ret = self.dup
|
153
|
+
ret.mat.scalar!(val)
|
154
|
+
ret
|
155
|
+
end
|
156
|
+
alias :* :scalar
|
157
|
+
def quo(val)
|
158
|
+
ret = self.dup
|
159
|
+
ret.mat.scalar!(1.quo(val))
|
160
|
+
ret
|
161
|
+
end
|
162
|
+
alias :/ :quo
|
163
|
+
|
164
|
+
def partial_derivative(variable)
|
165
|
+
case variable.to_s
|
166
|
+
when /\Ac/i
|
167
|
+
if @mat.col_size <= 1
|
168
|
+
self.class.new(Mat.new(1, 1, 0), 28, 0, 12, 12)
|
169
|
+
else
|
170
|
+
mat = Mat.new(@mat.row_size, @mat.col_size-1) do |i, j|
|
171
|
+
@mat.body[i][j+1] * (j+1)
|
172
|
+
end
|
173
|
+
self.class.new(mat, 28, 0, 12, 12)
|
174
|
+
end
|
175
|
+
when /\As/i
|
176
|
+
if @mat.row_size <= 1
|
177
|
+
self.class.new(Mat.new(1, 1, 0), 28, 0, 12, 12)
|
178
|
+
else
|
179
|
+
mat = Mat.new(@mat.row_size-1, @mat.col_size) do |i, j|
|
180
|
+
@mat.body[i+1][j] * (i+1)
|
181
|
+
end
|
182
|
+
self.class.new(mat, 28, 0, 12, 12)
|
183
|
+
end
|
184
|
+
else
|
185
|
+
raise ArgumentError, "the argument must be `s' or `c', not `#{variable}'"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
def smith_eff(smith, comp=smith)
|
189
|
+
partial_derivative('s').evaluate(smith, comp).quo(2*(smith-1))
|
190
|
+
end
|
191
|
+
def comp_eff(smith, comp=smith)
|
192
|
+
partial_derivative('c').evaluate(smith, comp).quo(4*(comp-1))
|
193
|
+
end
|
194
|
+
def eff(smith, comp=smith)
|
195
|
+
[smith_eff(smith, comp), comp_eff(smith, comp)]
|
196
|
+
end
|
197
|
+
|
198
|
+
def [](i, j)
|
199
|
+
if (i < 0 && @mat.body.size < -i) || (j < 0 && @mat.body[0].size < -j)
|
200
|
+
raise IndexError, "(#{i}, #{j}) is out of (#{@mat.body.size}, #{@mat.body[0].size})"
|
201
|
+
end
|
202
|
+
begin
|
203
|
+
ret = @mat.body[i][j]
|
204
|
+
rescue NoMethodError
|
205
|
+
return 0
|
206
|
+
end
|
207
|
+
ret.nil? ? 0 : ret
|
208
|
+
end
|
209
|
+
|
210
|
+
def coerce(other)
|
211
|
+
[self.class.new(Mat.new(1, 1, other), 28, 0, 12, 12), self]
|
212
|
+
end
|
213
|
+
def <(other)
|
214
|
+
foo = self-other
|
215
|
+
(foo.mat.row_size-1).downto(0) do |s|
|
216
|
+
(foo.mat.col_size-1).downto(0) do |c|
|
217
|
+
bar = foo.mat.body[s][c]
|
218
|
+
if bar < 0
|
219
|
+
return true
|
220
|
+
elsif 0 < bar
|
221
|
+
return false
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
false
|
226
|
+
end
|
227
|
+
def <=(other)
|
228
|
+
foo = self-other
|
229
|
+
(foo.mat.row_size-1).downto(0) do |s|
|
230
|
+
(foo.mat.col_size-1).downto(0) do |c|
|
231
|
+
bar = foo.mat.body[s][c]
|
232
|
+
if bar < 0
|
233
|
+
return true
|
234
|
+
elsif 0 < bar
|
235
|
+
return false
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
true
|
240
|
+
end
|
241
|
+
def >(other)
|
242
|
+
foo = other-self
|
243
|
+
(foo.mat.row_size-1).downto(0) do |s|
|
244
|
+
(foo.mat.col_size-1).downto(0) do |c|
|
245
|
+
bar = foo.mat.body[s][c]
|
246
|
+
if bar < 0
|
247
|
+
return true
|
248
|
+
elsif 0 < bar
|
249
|
+
return false
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
false
|
254
|
+
end
|
255
|
+
def >=(other)
|
256
|
+
foo = other-self
|
257
|
+
(foo.mat.row_size-1).downto(0) do |s|
|
258
|
+
(foo.mat.col_size-1).downto(0) do |c|
|
259
|
+
bar = foo.mat.body[s][c]
|
260
|
+
if bar < 0
|
261
|
+
return true
|
262
|
+
elsif 0 < bar
|
263
|
+
return false
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
true
|
268
|
+
end
|
269
|
+
def ==(other)
|
270
|
+
foo = self-other
|
271
|
+
(foo.mat.row_size-1).downto(0) do |s|
|
272
|
+
(foo.mat.col_size-1).downto(0) do |c|
|
273
|
+
bar = foo.mat.body[s][c]
|
274
|
+
return false if bar != 0
|
275
|
+
end
|
276
|
+
end
|
277
|
+
true
|
278
|
+
end
|
279
|
+
end
|
280
|
+
class << TPolynomial
|
281
|
+
ParamIndex = Hash.new
|
282
|
+
%i|attack phydef magdef hp mp str dex speed magic|.each.with_index do |s, i|
|
283
|
+
ParamIndex.store(s, i)
|
284
|
+
ParamIndex.store(i, i)
|
285
|
+
ParamIndex.store(Equip::ParamList[i], i)
|
286
|
+
end
|
287
|
+
def from_equip(equip, para)
|
288
|
+
new(Mat.new(1, 1, equip.para[ParamIndex[para]]), equip.kind, equip.star, equip.main, equip.sub)
|
289
|
+
end
|
290
|
+
def smith(str, para)
|
291
|
+
unless m = /\A(.+)\((.+\d+),?(.+\d+)\)\Z/.match(str)
|
292
|
+
raise ArgumentError.new("given string `#{str}' is unparsable as a smithing recipe")
|
293
|
+
end
|
294
|
+
kind = EquipIndex[m[1].to_sym]
|
295
|
+
main_m, main_s, main_mc = Equip.__send__(:parse_material, m[2])
|
296
|
+
sub_m, sub_s, sub_mc = Equip.__send__(:parse_material, m[3])
|
297
|
+
para = ParamIndex[para]
|
298
|
+
|
299
|
+
c = ( Equip9[kind][para] * Main9[main_m][para] ).cdiv(100).quo( main_mc==sub_mc ? 200 : 100 )
|
300
|
+
new(Mat.v_array(c*Sub9[sub_m][para], c), kind, (main_s+sub_s).div(2), main_mc, sub_mc)
|
301
|
+
end
|
302
|
+
def compose(main, sub, para)
|
303
|
+
main_k, sub_k = main.kind, sub.kind
|
304
|
+
main_s, sub_s = main.star, sub.star
|
305
|
+
main_main, sub_main = main.main, sub.main
|
306
|
+
main_sub, sub_sub = main.sub, sub.sub
|
307
|
+
para = ParamIndex[para]
|
308
|
+
|
309
|
+
if Equip9[main_k][para] == 0
|
310
|
+
c = 0.quo(1)
|
311
|
+
else
|
312
|
+
c = ( 100 + Equip9[main_k][para] - Equip9[sub_k][para] + Material9[main_main][para] - Material9[sub_main][para] +
|
313
|
+
(main_s-sub_s)*5 - ( ( main_main==sub_main && main_main != 9 ) ? 30 : 0 ) ).quo( main_k==sub_k ? 40000 : 20000 )
|
314
|
+
end
|
315
|
+
mat = main.mat.padd(sub.mat.pprod(Mat.h_array(c*Equip9[main_k][para], c)))
|
316
|
+
new(mat, main_k, main_s+sub_s, main_sub, sub_main)
|
317
|
+
end
|
318
|
+
def build(str, para, left_associative: true)
|
319
|
+
str = Mgmg.check_string(str)
|
320
|
+
_para = ParamIndex[para]
|
321
|
+
if _para.nil?
|
322
|
+
raise ArgumentError, "unknown parameter symbol `#{para.inspect}' given"
|
323
|
+
end
|
324
|
+
stack, str = build_sub0([], str, _para)
|
325
|
+
build_sub(stack, str, _para, left_associative)
|
326
|
+
end
|
327
|
+
private def build_sub0(stack, str, para)
|
328
|
+
SystemEquip.each do |k, v|
|
329
|
+
stack << from_equip(v, para)
|
330
|
+
str = str.gsub(k, "<#{stack.length-1}>")
|
331
|
+
end
|
332
|
+
[stack, str]
|
333
|
+
end
|
334
|
+
private def build_sub(stack, str, para, lassoc)
|
335
|
+
if m = /\A(.*\+?)\[([^\[\]]+)\](\+?[^\[]*)\Z/.match(str)
|
336
|
+
stack << build_sub(stack, m[2], para, lassoc)
|
337
|
+
build_sub(stack, "#{m[1]}<#{stack.length-1}>#{m[3]}", para, lassoc)
|
338
|
+
elsif m = ( lassoc ? /\A(.+)\+(.+?)\Z/ : /\A(.+?)\+(.+)\Z/ ).match(str)
|
339
|
+
compose(build_sub(stack, m[1], para, lassoc), build_sub(stack, m[2], para, lassoc), para)
|
340
|
+
elsif m = /\A\<(\d+)\>\Z/.match(str)
|
341
|
+
stack[m[1].to_i]
|
342
|
+
else
|
343
|
+
smith(str, para)
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
data/lib/mgmg/search.rb
ADDED
@@ -0,0 +1,254 @@
|
|
1
|
+
class String
|
2
|
+
def smith_search(para, target, comp, smith_min=nil, smith_max=10000, left_associative: true, cut_exp: Float::INFINITY, min_smith: false)
|
3
|
+
if smith_min.nil?
|
4
|
+
if min_smith
|
5
|
+
smith_min = self.min_smith.min_smith
|
6
|
+
else
|
7
|
+
smith_min = build(-1, -1, left_associative: left_associative).min_level
|
8
|
+
end
|
9
|
+
end
|
10
|
+
if smith_max < smith_min
|
11
|
+
raise ArgumentError, "smith_min <= smith_max is needed, (smith_min, smith_max) = (#{smith_min}, #{smith_max}) are given"
|
12
|
+
elsif cut_exp < Float::INFINITY
|
13
|
+
begin
|
14
|
+
smith_max = [smith_max, Mgmg.invexp2(cut_exp, comp)].min
|
15
|
+
rescue
|
16
|
+
raise Mgmg::SearchCutException
|
17
|
+
end
|
18
|
+
end
|
19
|
+
if target <= build(smith_min, comp, left_associative: left_associative).para_call(para)
|
20
|
+
return smith_min
|
21
|
+
elsif build(smith_max, comp, left_associative: left_associative).para_call(para) < target
|
22
|
+
raise Mgmg::SearchCutException
|
23
|
+
end
|
24
|
+
while 1 < smith_max - smith_min do
|
25
|
+
smith = (smith_max - smith_min).div(2) + smith_min
|
26
|
+
if build(smith, comp, left_associative: left_associative).para_call(para) < target
|
27
|
+
smith_min = smith
|
28
|
+
else
|
29
|
+
smith_max = smith
|
30
|
+
end
|
31
|
+
end
|
32
|
+
smith_max
|
33
|
+
end
|
34
|
+
def comp_search(para, target, smith, comp_min=nil, comp_max=10000, left_associative: true)
|
35
|
+
comp_min = min_comp(left_associative: left_associative)
|
36
|
+
if comp_max < comp_min
|
37
|
+
raise ArgumentError, "comp_min <= comp_max is needed, (comp_min, comp_max) = (#{comp_min}, #{comp_max}) are given"
|
38
|
+
end
|
39
|
+
if target <= build(smith, comp_min, left_associative: left_associative).para_call(para)
|
40
|
+
return comp_min
|
41
|
+
elsif build(smith, comp_max, left_associative: left_associative).para_call(para) < target
|
42
|
+
raise Mgmg::SearchCutException
|
43
|
+
end
|
44
|
+
while 1 < comp_max - comp_min do
|
45
|
+
comp = (comp_max - comp_min).div(2) + comp_min
|
46
|
+
if build(smith, comp, left_associative: left_associative).para_call(para) < target
|
47
|
+
comp_min = comp
|
48
|
+
else
|
49
|
+
comp_max = comp
|
50
|
+
end
|
51
|
+
end
|
52
|
+
comp_max
|
53
|
+
end
|
54
|
+
def search(para, target, smith_min=nil, comp_min=nil, smith_max=10000, comp_max=10000, left_associative: true, step: 1, cut_exp: Float::INFINITY, min_smith: false)
|
55
|
+
if smith_min.nil?
|
56
|
+
if min_smith
|
57
|
+
smith_min = self.min_smith
|
58
|
+
else
|
59
|
+
smith_min = build(-1, -1, left_associative: left_associative).min_level
|
60
|
+
end
|
61
|
+
end
|
62
|
+
comp_min = min_comp(left_associative: left_associative) if comp_min.nil?
|
63
|
+
comp_min = comp_search(para, target, smith_max, comp_min, comp_max, left_associative: left_associative)
|
64
|
+
smith_max = smith_search(para, target, comp_min, smith_min, smith_max, left_associative: left_associative)
|
65
|
+
smith_min = smith_search(para, target, comp_max, smith_min, smith_max, left_associative: left_associative)
|
66
|
+
raise Mgmg::SearchCutException if cut_exp < Mgmg.exp(smith_min, comp_min)
|
67
|
+
comp_max = comp_search(para, target, smith_min, comp_min, comp_max, left_associative: left_associative)
|
68
|
+
minex, ret = Mgmg.exp(smith_min, comp_max), [smith_min, comp_max]
|
69
|
+
exp = Mgmg.exp(smith_max, comp_min)
|
70
|
+
minex, ret = exp, [smith_max, comp_min] if exp < minex
|
71
|
+
(comp_min+step).step(comp_max-1, step) do |comp|
|
72
|
+
break if minex < Mgmg.exp(smith_min, comp)
|
73
|
+
smith = smith_search(para, target, comp, smith_min, smith_max, left_associative: left_associative, cut_exp: [minex, cut_exp].min)
|
74
|
+
exp = Mgmg.exp(smith, comp)
|
75
|
+
if exp < minex
|
76
|
+
minex, ret = exp, [smith, comp]
|
77
|
+
elsif exp == minex
|
78
|
+
if build(*ret).para_call(para) < build(smith, comp).para_call(para)
|
79
|
+
ret = [smith, comp]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
rescue Mgmg::SearchCutException
|
83
|
+
end
|
84
|
+
raise Mgmg::SearchCutException, "the result exceeds given cut_exp=#{cut_exp}" if cut_exp < minex
|
85
|
+
ret
|
86
|
+
end
|
87
|
+
end
|
88
|
+
module Enumerable
|
89
|
+
def smith_search(para, target, armor, comp, smith_min=nil, smith_max=10000, left_associative: true, cut_exp: Float::INFINITY, min_smith: false)
|
90
|
+
if smith_min.nil?
|
91
|
+
if min_smith
|
92
|
+
smith_min = self.min_smith[0]
|
93
|
+
else
|
94
|
+
smith_min = build(-1, -1, -1, left_associative: left_associative).min_level[0]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
if smith_max < smith_min
|
98
|
+
raise ArgumentError, "smith_min <= smith_max is needed, (smith_min, smith_max) = (#{smith_min}, #{smith_max}) are given"
|
99
|
+
elsif cut_exp < Float::INFINITY
|
100
|
+
begin
|
101
|
+
smith_max = [smith_max, Mgmg.invexp3(cut_exp, armor, comp)].min
|
102
|
+
rescue
|
103
|
+
raise Mgmg::SearchCutException
|
104
|
+
end
|
105
|
+
end
|
106
|
+
if build(smith_max, armor, comp, left_associative: left_associative).para_call(para) < target
|
107
|
+
raise Mgmg::SearchCutException
|
108
|
+
elsif target <= build(smith_min, armor, comp, left_associative: left_associative).para_call(para)
|
109
|
+
return smith_min
|
110
|
+
end
|
111
|
+
while 1 < smith_max - smith_min do
|
112
|
+
smith = (smith_max - smith_min).div(2) + smith_min
|
113
|
+
if build(smith, armor, comp, left_associative: left_associative).para_call(para) < target
|
114
|
+
smith_min = smith
|
115
|
+
else
|
116
|
+
smith_max = smith
|
117
|
+
end
|
118
|
+
end
|
119
|
+
smith_max
|
120
|
+
end
|
121
|
+
def armor_search(para, target, smith, comp, armor_min=nil, armor_max=10000, left_associative: true, cut_exp: Float::INFINITY, min_smith: false)
|
122
|
+
if armor_min.nil?
|
123
|
+
if min_smith
|
124
|
+
armor_min = self.min_smith[1]
|
125
|
+
else
|
126
|
+
armor_min = build(-1, -1, -1, left_associative: left_associative).min_level[1]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
if armor_max < armor_min
|
130
|
+
raise ArgumentError, "armor_min <= armor_max is needed, (armor_min, armor_max) = (#{armor_min}, #{armor_max}) are given"
|
131
|
+
elsif cut_exp < Float::INFINITY
|
132
|
+
begin
|
133
|
+
armor_max = [armor_max, Mgmg.invexp3(cut_exp, smith, comp)].min
|
134
|
+
rescue
|
135
|
+
raise Mgmg::SearchCutException
|
136
|
+
end
|
137
|
+
end
|
138
|
+
if build(smith, armor_max, comp, left_associative: left_associative).para_call(para) < target
|
139
|
+
raise Mgmg::SearchCutException
|
140
|
+
elsif target <= build(smith, armor_min, comp, left_associative: left_associative).para_call(para)
|
141
|
+
return armor_min
|
142
|
+
end
|
143
|
+
while 1 < armor_max - armor_min do
|
144
|
+
armor = (armor_max - armor_min).div(2) + armor_min
|
145
|
+
if build(smith, armor, comp, left_associative: left_associative).para_call(para) < target
|
146
|
+
armor_min = armor
|
147
|
+
else
|
148
|
+
armor_max = armor
|
149
|
+
end
|
150
|
+
end
|
151
|
+
armor_max
|
152
|
+
end
|
153
|
+
def sa_search(para, target, comp, smith_min=nil, armor_min=nil, smith_max=10000, armor_max=10000, left_associative: true, cut_exp: Float::INFINITY, min_smith: false)
|
154
|
+
if min_smith
|
155
|
+
s, a = self.min_smith
|
156
|
+
else
|
157
|
+
s, a = build(-1, -1, -1, left_associative: left_associative).min_level
|
158
|
+
end
|
159
|
+
smith_min = s if smith_min.nil?
|
160
|
+
armor_min = a if armor_min.nil?
|
161
|
+
smith_min = smith_search(para, target, armor_max, comp, smith_min, smith_max, left_associative: true)
|
162
|
+
armor_min = armor_search(para, target, smith_max, comp, armor_min, armor_max, left_associative: true)
|
163
|
+
raise Mgmg::SearchCutException if cut_exp < Mgmg.exp(smith_min, armor_min, comp)
|
164
|
+
smith_max = smith_search(para, target, armor_min, comp, smith_min, smith_max, left_associative: true)
|
165
|
+
armor_max = armor_search(para, target, smith_min, comp, armor_min, armor_max, left_associative: true)
|
166
|
+
minex, ret = Mgmg.exp(smith_min, armor_max, comp), [smith_min, armor_max]
|
167
|
+
exp = Mgmg.exp(smith_max, armor_min, comp)
|
168
|
+
if exp < minex
|
169
|
+
minex, ret = exp, [smith_max, armor_min]
|
170
|
+
(armor_min+1).upto(armor_max-1) do |armor|
|
171
|
+
break if minex < Mgmg.exp(smith_min, armor, comp)
|
172
|
+
smith = smith_search(para, target, armor, comp, smith_min, smith_max, left_associative: left_associative, cut_exp: [minex, cut_exp].min)
|
173
|
+
exp = Mgmg.exp(smith, armor, comp)
|
174
|
+
if exp < minex
|
175
|
+
minex, ret = exp, [smith, armor]
|
176
|
+
elsif exp == minex
|
177
|
+
if build(*ret, comp).para_call(para) < build(smith, armor, comp).para_call(para)
|
178
|
+
ret = [smith, armor]
|
179
|
+
end
|
180
|
+
end
|
181
|
+
rescue Mgmg::SearchCutException
|
182
|
+
end
|
183
|
+
else
|
184
|
+
(smith_min+1).upto(smith_max-1) do |smith|
|
185
|
+
break if minex < Mgmg.exp(smith, armor_min, comp)
|
186
|
+
armor = armor_search(para, target, smith, comp, armor_min, armor_max, left_associative: left_associative, cut_exp: [minex, cut_exp].min)
|
187
|
+
exp = Mgmg.exp(smith, armor, comp)
|
188
|
+
if exp < minex
|
189
|
+
minex, ret = exp, [smith, armor]
|
190
|
+
elsif exp == minex
|
191
|
+
if build(*ret, comp).para_call(para) < build(smith, armor, comp).para_call(para)
|
192
|
+
ret = [smith, armor]
|
193
|
+
end
|
194
|
+
end
|
195
|
+
rescue Mgmg::SearchCutException
|
196
|
+
end
|
197
|
+
end
|
198
|
+
raise Mgmg::SearchCutException if cut_exp < minex
|
199
|
+
ret
|
200
|
+
end
|
201
|
+
def comp_search(para, target, smith, armor, comp_min=nil, comp_max=10000, left_associative: true)
|
202
|
+
comp_min = min_comp(left_associative: left_associative)
|
203
|
+
if comp_max < comp_min
|
204
|
+
raise ArgumentError, "comp_min <= comp_max is needed, (comp_min, comp_max) = (#{comp_min}, #{comp_max}) are given"
|
205
|
+
end
|
206
|
+
if target <= build(smith, armor, comp_min, left_associative: left_associative).para_call(para)
|
207
|
+
return comp_min
|
208
|
+
elsif build(smith, armor, comp_max, left_associative: left_associative).para_call(para) < target
|
209
|
+
raise ArgumentError, "given comp_max=#{comp_max} does not satisfies the target"
|
210
|
+
end
|
211
|
+
while 1 < comp_max - comp_min do
|
212
|
+
comp = (comp_max - comp_min).div(2) + comp_min
|
213
|
+
if build(smith, armor, comp, left_associative: left_associative).para_call(para) < target
|
214
|
+
comp_min = comp
|
215
|
+
else
|
216
|
+
comp_max = comp
|
217
|
+
end
|
218
|
+
end
|
219
|
+
comp_max
|
220
|
+
end
|
221
|
+
def search(para, target, smith_min=nil, armor_min=nil, comp_min=nil, smith_max=10000, armor_max=10000, comp_max=10000, left_associative: true, cut_exp: Float::INFINITY, min_smith: false)
|
222
|
+
if min_smith
|
223
|
+
s, a = self.min_smith
|
224
|
+
else
|
225
|
+
s, a = build(-1, -1, -1, left_associative: left_associative).min_level
|
226
|
+
end
|
227
|
+
smith_min = s if smith_min.nil?
|
228
|
+
armor_min = a if armor_min.nil?
|
229
|
+
comp_min = min_comp(left_associative: left_associative) if comp_min.nil?
|
230
|
+
comp_min = comp_search(para, target, smith_max, armor_max, comp_min, comp_max, left_associative: left_associative)
|
231
|
+
smith_max, armor_max = sa_search(para, target, comp_min, smith_min, armor_min, smith_max, armor_max, left_associative: left_associative)
|
232
|
+
smith_min, armor_min = sa_search(para, target, comp_max, smith_min, armor_min, smith_max, armor_max, left_associative: left_associative)
|
233
|
+
raise Mgmg::SearchCutException if cut_exp < Mgmg.exp(smith_min, armor_min, comp_min)
|
234
|
+
comp_max = comp_search(para, target, smith_min, armor_min, comp_min, comp_max, left_associative: left_associative)
|
235
|
+
minex, ret = Mgmg.exp(smith_min, armor_min, comp_max), [smith_min, armor_min, comp_max]
|
236
|
+
exp = Mgmg.exp(smith_max, armor_max, comp_min)
|
237
|
+
minex, ret = exp, [smith_max, armor_max, comp_min] if exp < minex
|
238
|
+
(comp_min+1).upto(comp_max-1) do |comp|
|
239
|
+
break if minex < Mgmg.exp(smith_min, armor_min, comp)
|
240
|
+
smith, armor = sa_search(para, target, comp, smith_min, armor_min, smith_max, armor_max, left_associative: left_associative, cut_exp: [minex, cut_exp].min)
|
241
|
+
exp = Mgmg.exp(smith, armor, comp)
|
242
|
+
if exp < minex
|
243
|
+
minex, ret = exp, [smith, armor, comp]
|
244
|
+
elsif exp == minex
|
245
|
+
if build(*ret).para_call(para) < build(smith, armor, comp).para_call(para)
|
246
|
+
ret = [smith, armor, comp]
|
247
|
+
end
|
248
|
+
end
|
249
|
+
rescue Mgmg::SearchCutException
|
250
|
+
end
|
251
|
+
raise Mgmg::SearchCutException, "the result exceeds given cut_exp=#{cut_exp}" if cut_exp < minex
|
252
|
+
ret
|
253
|
+
end
|
254
|
+
end
|