mgmg 1.4.1 → 1.5.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 +23 -0
- data/README.md +51 -10
- data/lib/mgmg/cuisine.rb +140 -0
- data/lib/mgmg/equip.rb +19 -24
- data/lib/mgmg/ir.rb +401 -373
- data/lib/mgmg/optimize.rb +21 -9
- data/lib/mgmg/option.rb +84 -0
- data/lib/mgmg/poly.rb +2 -2
- data/lib/mgmg/recipe.rb +172 -0
- data/lib/mgmg/reinforce.rb +70 -0
- data/lib/mgmg/search.rb +204 -222
- data/lib/mgmg/system_equip.rb +6 -0
- data/lib/mgmg/utils.rb +70 -2
- data/lib/mgmg/version.rb +1 -1
- data/lib/mgmg.rb +186 -70
- data/mgmg.gemspec +4 -4
- data/reference.md +271 -49
- metadata +14 -10
data/lib/mgmg/optimize.rb
CHANGED
@@ -2,8 +2,12 @@ module Mgmg
|
|
2
2
|
module Optimize; end
|
3
3
|
class << Optimize
|
4
4
|
InvList = [%w|帽子 フード サンダル|.freeze, %w|宝1 骨1 木1 木2 骨2|.freeze, %w|宝1 骨1 木1|.freeze].freeze
|
5
|
-
def phydef_optimize(str, smith, comp=smith,
|
6
|
-
best =
|
5
|
+
def phydef_optimize(str, smith, comp=smith, opt: Option.new)
|
6
|
+
best = if smith.nil? then
|
7
|
+
[str, str.poly(:phydef, opt: opt), str.poly(:magdef, opt: opt), str.poly(:cost, opt: opt)]
|
8
|
+
else
|
9
|
+
[str, str.build(smith, comp, opt: opt)]
|
10
|
+
end
|
7
11
|
str = Mgmg.check_string(str)
|
8
12
|
ai = 0
|
9
13
|
while str.sub!(/(帽子|フード|サンダル)\([宝木骨][12][宝木骨]1\)/){
|
@@ -19,7 +23,7 @@ module Mgmg
|
|
19
23
|
m = /([^\+]*\([^\(]+[綿皮]1\))\]*\Z/.match(str)
|
20
24
|
if m
|
21
25
|
if smith
|
22
|
-
if m[1].sub(/綿1\)/, '皮1)').build(smith).weight == m[1].sub(/皮1\)/, '綿1)').build(smith).weight
|
26
|
+
if m[1].sub(/綿1\)/, '皮1)').build(smith, opt: opt).weight == m[1].sub(/皮1\)/, '綿1)').build(smith, opt: opt).weight
|
23
27
|
skin = true
|
24
28
|
end
|
25
29
|
else
|
@@ -35,7 +39,11 @@ module Mgmg
|
|
35
39
|
b = b0
|
36
40
|
while b
|
37
41
|
r = pd_apply_idx(str, a, b)
|
38
|
-
best =
|
42
|
+
best = if smith.nil? then
|
43
|
+
pd_better(best, [r, r.poly(:phydef, opt: opt), r.poly(:magdef, opt: opt), r.poly(:cost, opt: opt)], opt.magdef_maximize)
|
44
|
+
else
|
45
|
+
pd_better(best, [r, r.build(smith, comp, opt: opt)], opt.magdef_maximize)
|
46
|
+
end
|
39
47
|
b = pd_next_b(b)
|
40
48
|
end
|
41
49
|
a = pd_next_a(a)
|
@@ -49,7 +57,11 @@ module Mgmg
|
|
49
57
|
b = b0
|
50
58
|
while b
|
51
59
|
r = pd_apply_idx(str, a, b)
|
52
|
-
best =
|
60
|
+
best = if smith.nil? then
|
61
|
+
pd_better(best, [r, r.poly(:phydef, opt: opt), r.poly(:magdef, opt: opt), r.poly(:cost, opt: opt)], opt.magdef_maximize)
|
62
|
+
else
|
63
|
+
pd_better(best, [r, r.build(smith, comp, opt: opt)], opt.magdef_maximize)
|
64
|
+
end
|
53
65
|
b = pd_next_b(b)
|
54
66
|
end
|
55
67
|
a = pd_next_a(a)
|
@@ -102,7 +114,7 @@ module Mgmg
|
|
102
114
|
end
|
103
115
|
return pre
|
104
116
|
else
|
105
|
-
raise
|
117
|
+
raise UnexpectedError
|
106
118
|
end
|
107
119
|
end
|
108
120
|
private def pd_apply_idx(str, a, b)
|
@@ -146,8 +158,8 @@ module Mgmg
|
|
146
158
|
end
|
147
159
|
|
148
160
|
MwList = %w|綿 皮 骨 木 水|.freeze
|
149
|
-
def buster_optimize(str, smith, comp=smith,
|
150
|
-
best = ( smith.nil? ? [str, str.poly(:mag_das)] : [str, str.build(smith, comp)] )
|
161
|
+
def buster_optimize(str, smith, comp=smith, opt: Option.new)
|
162
|
+
best = ( smith.nil? ? [str, str.poly(:mag_das, opt: opt)] : [str, str.build(smith, comp, opt: opt)] )
|
151
163
|
str = Mgmg.check_string(str)
|
152
164
|
ai = -1
|
153
165
|
org = nil
|
@@ -162,7 +174,7 @@ module Mgmg
|
|
162
174
|
a = Array.new(ai){ [0, 0, 0] }
|
163
175
|
while a
|
164
176
|
r = bus_apply_idx(str, a)
|
165
|
-
best = bus_better(best, ( smith.nil? ? [r, r.poly(:mag_das)] : [r, r.build(smith, comp)] ))
|
177
|
+
best = bus_better(best, ( smith.nil? ? [r, r.poly(:mag_das, opt: opt)] : [r, r.build(smith, comp, opt: opt)] ))
|
166
178
|
a = bus_next_a(a)
|
167
179
|
end
|
168
180
|
best[0]
|
data/lib/mgmg/option.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
module Mgmg
|
2
|
+
class Option
|
3
|
+
def initialize(
|
4
|
+
left_associative: true,
|
5
|
+
smith_min: nil, armor_min:nil, comp_min: nil, smith_max: 10000, armor_max: 10000, comp_max: 10000,
|
6
|
+
step: 1, magdef_maximize: true,
|
7
|
+
target_weight: 0, reinforcement: [], buff: nil,
|
8
|
+
irep: nil, cut_exp: Float::INFINITY
|
9
|
+
)
|
10
|
+
@left_associative = left_associative
|
11
|
+
@smith_min = smith_min
|
12
|
+
@armor_min = armor_min
|
13
|
+
@comp_min = comp_min
|
14
|
+
@smith_max = smith_max
|
15
|
+
@armor_max = armor_max
|
16
|
+
@comp_max = comp_max
|
17
|
+
@step = step
|
18
|
+
@magdef_maximize = magdef_maximize
|
19
|
+
@target_weight = target_weight
|
20
|
+
@reinforcement = reinforcement
|
21
|
+
unless buff.nil?
|
22
|
+
if @reinforcement.empty?
|
23
|
+
@reinforcement = buff
|
24
|
+
else
|
25
|
+
raise ArgumentError, "reinforcement and buff are exclusive"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
@irep = irep
|
29
|
+
@cut_exp = cut_exp
|
30
|
+
end
|
31
|
+
attr_accessor :left_associative, :smith_min, :armor_min, :comp_min, :smith_max, :armor_max, :comp_max
|
32
|
+
attr_accessor :step, :magdef_maximize, :target_weight, :reinforcement, :irep, :cut_exp
|
33
|
+
def initialize_copy(other)
|
34
|
+
@left_associative = other.left_associative
|
35
|
+
@smith_min = other.smith_min
|
36
|
+
@armor_min = other.armor_min
|
37
|
+
@comp_min = other.comp_min
|
38
|
+
@smith_max = other.smith_max
|
39
|
+
@armor_max = other.armor_max
|
40
|
+
@comp_max = other.comp_max
|
41
|
+
@step = other.step
|
42
|
+
@magdef_maximize = other.magdef_maximize
|
43
|
+
@target_weight = other.target_weight
|
44
|
+
@reinforcement = other.reinforcement.dup
|
45
|
+
@irep = other.irep
|
46
|
+
@cut_exp = other.cut_exp
|
47
|
+
end
|
48
|
+
def update_sa_min(recipe, force=true)
|
49
|
+
case recipe
|
50
|
+
when String
|
51
|
+
if @smith_min.nil? && @armor_min
|
52
|
+
@smith_min = @armor_min
|
53
|
+
end
|
54
|
+
if force || @smith_min.nil?
|
55
|
+
s = recipe.min_level(@target_weight, opt: self)
|
56
|
+
@smith_min = s if force || @smith_min.nil?
|
57
|
+
@armor_min = s if force || @armor_min.nil?
|
58
|
+
end
|
59
|
+
when Enumerable
|
60
|
+
if force || @smith_min.nil? || @armor_min.nil?
|
61
|
+
@target_weight = [@target_weight, @target_weight] if @target_weight.kind_of? Numeric
|
62
|
+
s, a = recipe.min_level(*@target_weight, opt: self)
|
63
|
+
@smith_min = s if force || @smith_min.nil?
|
64
|
+
@armor_min = a if force || @armor_min.nil?
|
65
|
+
end
|
66
|
+
else
|
67
|
+
raise ArgumentError, 'recipe should be String or Enumerable'
|
68
|
+
end
|
69
|
+
self
|
70
|
+
end
|
71
|
+
def set_default(recipe, force: false)
|
72
|
+
update_sa_min(recipe, force)
|
73
|
+
@comp_min = recipe.min_comp(opt: self) if force || @comp_min.nil?
|
74
|
+
@irep = recipe.ir(opt: self) if force || @irep.nil?
|
75
|
+
self
|
76
|
+
end
|
77
|
+
def buff
|
78
|
+
@reinforcement
|
79
|
+
end
|
80
|
+
def buff=(v)
|
81
|
+
@reinforcement = v
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/mgmg/poly.rb
CHANGED
@@ -301,8 +301,8 @@ module Mgmg
|
|
301
301
|
raise ArgumentError.new("given string `#{str}' is unparsable as a smithing recipe")
|
302
302
|
end
|
303
303
|
kind = EquipIndex[m[1].to_sym]
|
304
|
-
main_m, main_s, main_mc =
|
305
|
-
sub_m, sub_s, sub_mc =
|
304
|
+
main_m, main_s, main_mc = Mgmg.parse_material(m[2])
|
305
|
+
sub_m, sub_s, sub_mc = Mgmg.parse_material(m[3])
|
306
306
|
para = ParamIndex[para]
|
307
307
|
|
308
308
|
c = ( Equip9[kind][para] * Main9[main_m][para] ).cdiv(100).quo( main_mc==sub_mc ? 200 : 100 )
|
data/lib/mgmg/recipe.rb
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
module Mgmg
|
2
|
+
class Recipe
|
3
|
+
def initialize(recipe, para=:power, **kw)
|
4
|
+
@recipe = recipe
|
5
|
+
@recipe.each(&:freeze) if @recipe.kind_of?(Enumerable)
|
6
|
+
@recipe.freeze
|
7
|
+
@para = para
|
8
|
+
@option = Option.new(**kw).set_default(@recipe)
|
9
|
+
end
|
10
|
+
attr_reader :recipe
|
11
|
+
attr_accessor :para
|
12
|
+
def initialize_copy(other)
|
13
|
+
@recipe = other.recipe.dup
|
14
|
+
@option = other.option.dup
|
15
|
+
end
|
16
|
+
private def temp_opt(**kw)
|
17
|
+
if kw.empty?
|
18
|
+
@option
|
19
|
+
else
|
20
|
+
ret = @option.dup
|
21
|
+
kw.each do |key, value|
|
22
|
+
ret.method((key.to_s+'=').to_sym).call(value)
|
23
|
+
ret.update_sa_min(@recipe) if key == :target_weight
|
24
|
+
ret.irep.add_reinforcement(value) if key == :reinforcement || key == :buff
|
25
|
+
if key == :left_associative
|
26
|
+
ret.irep = @recipe.ir(opt: ret).add_reinforcement(ret.reinforcement)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
ret
|
30
|
+
end
|
31
|
+
end
|
32
|
+
def option(**kw)
|
33
|
+
@option = temp_opt(*kw)
|
34
|
+
@option
|
35
|
+
end
|
36
|
+
def option=(new_option)
|
37
|
+
@option = new_option.set_default(@recipe)
|
38
|
+
end
|
39
|
+
def replace(new_recipe, para: @para, **kw)
|
40
|
+
@recipe = new_recipe
|
41
|
+
@recipe.each(&:freeze) if @recipe.kind_of?(Enumerable)
|
42
|
+
@recipe.freeze
|
43
|
+
@para = para
|
44
|
+
@option = Option.new(**kw).set_default(@recipe)
|
45
|
+
self
|
46
|
+
end
|
47
|
+
def min_weight
|
48
|
+
@recipe.min_weight(opt: @option)
|
49
|
+
end
|
50
|
+
def max_weight(include_outsourcing=false)
|
51
|
+
@recipe.max_weight(opt: @option)
|
52
|
+
end
|
53
|
+
def min_level(w=@option.target_weight, include_outsourcing=false)
|
54
|
+
@recipe.min_level(w, include_outsourcing, opt: @option)
|
55
|
+
end
|
56
|
+
def min_levels(w=1)
|
57
|
+
@recipe.min_levels(w, opt: @option)
|
58
|
+
end
|
59
|
+
def min_levels_max(w=1)
|
60
|
+
@recipe.min_levels_max(w, opt: @option)
|
61
|
+
end
|
62
|
+
def min_smith
|
63
|
+
@recipe.min_smith
|
64
|
+
end
|
65
|
+
def min_comp
|
66
|
+
@recipe.min_comp
|
67
|
+
end
|
68
|
+
def build(smith=-1, armor=smith, comp=armor.tap{armor=smith}, **kw)
|
69
|
+
opt = temp_opt(**kw)
|
70
|
+
smith, armor, comp = opt.smith_min, opt.armor_min, opt.comp_min if smith.nil?
|
71
|
+
case @recipe
|
72
|
+
when String
|
73
|
+
recipe.build(smith, comp, opt: opt)
|
74
|
+
when Enumerable
|
75
|
+
recipe.build(smith, armor, comp, opt: opt)
|
76
|
+
else
|
77
|
+
raise BrokenRecipeError
|
78
|
+
end
|
79
|
+
end
|
80
|
+
def show(smith=-1, armor=smith, comp=armor.tap{armor=smith}, para: @para, **kw)
|
81
|
+
opt = temp_opt(**kw)
|
82
|
+
smith, armor, comp = opt.smith_min, opt.armor_min, opt.comp_min if smith.nil?
|
83
|
+
case @recipe
|
84
|
+
when String
|
85
|
+
recipe.show(smith, comp, para: para, opt: opt)
|
86
|
+
when Enumerable
|
87
|
+
recipe.show(smith, armor, comp, para: para, opt: opt)
|
88
|
+
else
|
89
|
+
raise BrokenRecipeError
|
90
|
+
end
|
91
|
+
end
|
92
|
+
def search(target, para: @para, **kw)
|
93
|
+
opt = temp_opt(**kw)
|
94
|
+
@recipe.search(para, target, opt: opt)
|
95
|
+
end
|
96
|
+
private def correct_level(s, ac, x, opt)
|
97
|
+
if s.nil?
|
98
|
+
if x.equal?(false)
|
99
|
+
s, ac, x = opt.smith_min, opt.comp_min, nil
|
100
|
+
else
|
101
|
+
s, ac, x = opt.smith_min, opt.armor_min, opt.comp_min
|
102
|
+
end
|
103
|
+
else
|
104
|
+
s = 0 if s < 0
|
105
|
+
ac = 0 if ac < 0
|
106
|
+
if x.equal?(false)
|
107
|
+
x = nil
|
108
|
+
else
|
109
|
+
x = 0 if x < 0
|
110
|
+
end
|
111
|
+
end
|
112
|
+
[s, ac, x]
|
113
|
+
end
|
114
|
+
def para_call(smith=-1, armor=smith, comp=armor.tap{armor=smith}, para: @para, **kw)
|
115
|
+
opt = temp_opt(**kw)
|
116
|
+
smith, armor, comp = correct_level(smith, armor, comp, opt)
|
117
|
+
case @recipe
|
118
|
+
when String
|
119
|
+
opt.irep.para_call(para, smith, comp)
|
120
|
+
when Enumerable
|
121
|
+
opt.irep.para_call(para, smith, armor, comp)
|
122
|
+
else
|
123
|
+
raise InvalidRecipeError
|
124
|
+
end
|
125
|
+
end
|
126
|
+
def ir(**kw)
|
127
|
+
temp_opt(**kw).irep
|
128
|
+
end
|
129
|
+
%i|attack phydef magdef hp mp str dex speed magic atkstr atk_sd dex_as mag_das magic2 magmag pmdef|.each do |sym|
|
130
|
+
define_method(sym) do |s, ac=s, x=false, **kw|
|
131
|
+
s, ac, x = correct_level(s, ac, x, temp_opt(**kw))
|
132
|
+
ir(**kw).method(sym).call(s, ac, x)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
%i|power fpower|.each do |sym|
|
136
|
+
define_method(sym) do |s, a=s, c=a.tap{a=s}, **kw|
|
137
|
+
s, a, c = correct_level(s, a, c, temp_opt(**kw))
|
138
|
+
ir(**kw).method(sym).call(s, a, c)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
%i|smith_cost comp_cost cost|.each do |sym|
|
142
|
+
define_method(sym) do |s, c=s, outsourcing=false, **kw|
|
143
|
+
s, c, x = correct_level(s, c, false, temp_opt(**kw))
|
144
|
+
ir(**kw).method(sym).call(s, c, out_sourcing)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
def poly(para=@para, **kw)
|
148
|
+
opt = temp_opt(**kw)
|
149
|
+
if @recipe.kind_of?(String)
|
150
|
+
@recipe.poly(para, opt: opt)
|
151
|
+
else
|
152
|
+
raise InvalidRecipeError, "Mgmg::Recipe#poly is available only for String recipes."
|
153
|
+
end
|
154
|
+
end
|
155
|
+
def phydef_optimize(smith=nil, comp=smith, **kw)
|
156
|
+
opt = temp_opt(**kw)
|
157
|
+
if @recipe.kind_of?(String)
|
158
|
+
@recipe.phydef_optimize(smith, comp, opt: opt)
|
159
|
+
else
|
160
|
+
raise InvalidRecipeError, "Mgmg::Recipe#phydef_optimize is available only for String recipes."
|
161
|
+
end
|
162
|
+
end
|
163
|
+
def buster_optimize(smith=nil, comp=smith, **kw)
|
164
|
+
opt = temp_opt(**kw)
|
165
|
+
if @recipe.kind_of?(String)
|
166
|
+
@recipe.buster_optimize(smith, comp, opt: opt)
|
167
|
+
else
|
168
|
+
raise InvalidRecipeError, "Mgmg::Recipe#buster_optimize is available only for String recipes."
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Mgmg
|
2
|
+
class Reinforcement
|
3
|
+
def initialize(vec)
|
4
|
+
@vec = vec
|
5
|
+
end
|
6
|
+
attr_accessor :vec
|
7
|
+
def initialize_copy(other)
|
8
|
+
@vec = other.vec.dup
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
'(' + @vec.map.with_index{|e, i| e==0 ? nil : "#{Equip::ParamList[i]}:#{e}"}.compact.join(', ') + ')'
|
13
|
+
end
|
14
|
+
alias :inspect :to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
# スキル名 攻 物 防 HP MP 腕 器 速 魔
|
18
|
+
Skill = {
|
19
|
+
'物防御UP' => Reinforcement.new( Vec[ 0, 10, 0, 0, 0, 0, 0, 0, 0] ),
|
20
|
+
'魔防御UP' => Reinforcement.new( Vec[ 0, 0, 10, 0, 0, 0, 0, 0, 0] ),
|
21
|
+
'腕力UP' => Reinforcement.new( Vec[ 0, 0, 0, 0, 0, 10, 0, 0, 0] ),
|
22
|
+
'メンテナンス' => Reinforcement.new( Vec[ 50, 0, 0, 0, 0, 0, 0, 0, 0] ),
|
23
|
+
'ガードアップ' => Reinforcement.new( Vec[ 0, 50, 0, 0, 0, 0, 0, 0, 0] ),
|
24
|
+
'パワーアップ' => Reinforcement.new( Vec[ 0, 0, 0, 0, 0, 50, 0, 0, 0] ),
|
25
|
+
'デックスアップ' => Reinforcement.new( Vec[ 0, 0, 0, 0, 0, 0, 50, 0, 0] ),
|
26
|
+
'スピードアップ' => Reinforcement.new( Vec[ 0, 0, 0, 0, 0, 0, 0, 50, 0] ),
|
27
|
+
'マジックアップ' => Reinforcement.new( Vec[ 0, 0, 0, 0, 0, 0, 0, 0, 50] ),
|
28
|
+
'オールアップ' => Reinforcement.new( Vec[ 0, 50, 0, 0, 0, 50, 50, 50, 50] ),
|
29
|
+
}
|
30
|
+
|
31
|
+
class << Reinforcement
|
32
|
+
def cuisine(c)
|
33
|
+
Reinforcement.new( Vec[*(c.vec), *Array.new(6, 0)] )
|
34
|
+
end
|
35
|
+
def compile(arg)
|
36
|
+
case arg
|
37
|
+
when Reinforcement
|
38
|
+
arg
|
39
|
+
when Cuisine
|
40
|
+
cuisine(arg)
|
41
|
+
when String
|
42
|
+
if Skill.has_key?(arg)
|
43
|
+
Skill[arg]
|
44
|
+
elsif SystemCuisine.has_key?(arg)
|
45
|
+
cuisine(SystemCuisine[arg])
|
46
|
+
else
|
47
|
+
raise InvalidReinforcementNameError, arg
|
48
|
+
end
|
49
|
+
else
|
50
|
+
raise ArgumentError, "The argument should be Mgmg::Cuisine or skill name String. (`#{arg}' is given)"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class Equip
|
56
|
+
def reinforce(*arg)
|
57
|
+
arg.each do |r|
|
58
|
+
r = Reinforcement.compile(r)
|
59
|
+
@para.map!.with_index do |pr, i|
|
60
|
+
if r.vec[i] == 0
|
61
|
+
pr
|
62
|
+
else
|
63
|
+
pr * (100+r.vec[i]).quo(100)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
self
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|