mgmg 1.4.2 → 1.5.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.
data/lib/mgmg/utils.rb CHANGED
@@ -17,16 +17,33 @@ module Mgmg
17
17
  refine Float do
18
18
  alias :cdiv :quo # Floatの場合は普通の割り算
19
19
  def comma3
20
- s = self.to_s
21
- case s
22
- when %r|e|
23
- s
24
- when %r|\.|
25
- ary = s.split('.')
26
- ary[0].gsub(/(\d)(?=(\d{3})+(?!\d))/, '\1,') + '.' + ary[1]
20
+ s = (self*100).round.to_s
21
+ if s[0] == '-'
22
+ g, s = '-', s[1..(-1)]
27
23
  else
28
- s
24
+ g = ''
29
25
  end
26
+ raise unless %r|\A\d+\Z|.match(s)
27
+ case s.length
28
+ when 1
29
+ if s == '0'
30
+ '0.0'
31
+ else
32
+ g+'0.0'+s
33
+ end
34
+ when 2
35
+ if s[1] == '0'
36
+ g+'0.'+s[0]
37
+ else
38
+ g+'0.'+s
39
+ end
40
+ else
41
+ i, d = s[0..(-3)], s[(-2)..(-1)]
42
+ d = d[0] if d[1] == '0'
43
+ g+i.gsub(/(\d)(?=(\d{3})+(?!\d))/, '\1,') + '.' + d
44
+ end
45
+ rescue
46
+ self.to_s
30
47
  end
31
48
  end
32
49
  refine Rational do
@@ -71,6 +88,27 @@ module Mgmg
71
88
  end
72
89
  attr_accessor :equip
73
90
  end
91
+ class InvalidReinforcementNameError < StandardError
92
+ def initialize(str)
93
+ @name = str
94
+ super("Unknown skill or preset cuisine name `#{@name}' is given.")
95
+ end
96
+ attr_accessor :name
97
+ end
98
+ class InvalidRecipeError < StandardError
99
+ def initialize(msg=nil)
100
+ if msg.nil?
101
+ super("Neither String nor Enumerable recipe was set.")
102
+ else
103
+ super(msg)
104
+ end
105
+ end
106
+ end
107
+ class Over20Error < StandardError
108
+ def initialize(star)
109
+ super("The star of given recipe is #{star}. It can't be built since the star is over 20.")
110
+ end
111
+ end
74
112
  class SearchCutException < StandardError; end
75
113
  class UnexpectedError < StandardError
76
114
  def initialize()
@@ -115,8 +153,13 @@ module Mgmg
115
153
  module_function def invexp3(exp, sa, comp)
116
154
  Math.sqrt(exp - ((sa-1)**2) - (2*((comp-1)**2)) - 4).round + 1
117
155
  end
156
+ module_function def option(recipe=nil, **kw)
157
+ ret = Option.new(**kw)
158
+ ret.set_default(recipe) unless recipe.nil?
159
+ ret
160
+ end
118
161
 
119
- CharacterList = /[^\(\)\+0123456789\[\]あきくしすたてなねのびりるイウガクグサジスタダチツデトドニノフブペボムラリルロンヴー一万二光兜典刀剣劣匠双古名吹咆品哮地大天太子安宝小帽弓弩当息悪戦手指斧書服木本杖業樹歴殺水氷法火炎牙物玉王産用界異的皮盾短石砕竜紫綿耳聖脛腕腿般良色衣袋覇質軍軽輝輪重量金鉄鎧闇陽靴額飾首骨鬼龍]/
162
+ CharacterList = /[^\(\)\+0123456789\[\]あきくしすたてなねのびりるイウガクグサジスタダチツデトドニノフブペボムラリルロンヴー一万二光兜典刀剣劣匠双古名吹咆品哮地大天太子安宝小帽弓弩当息悪戦手指斧書服木本杖業樹歴殺水氷法火炎牙物玉王産用界異的皮盾短石砕竜紫綿耳聖脛腕腿般良色衣袋覇質軍軽輝輪重量金鉄鎧闇陽靴額飾首骨鬼龍]/.freeze
120
163
  module_function def check_string(str)
121
164
  str = str.gsub(/[\s \\]/, '')
122
165
  if m = CharacterList.match(str)
@@ -159,7 +202,7 @@ module Mgmg
159
202
  end
160
203
  str
161
204
  end
162
-
205
+
163
206
  module_function def parse_material(str)
164
207
  m = /\A.+?(\d+)\Z/.match(str)
165
208
  mat = MaterialIndex[str.to_sym]
data/lib/mgmg/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Mgmg
2
- VERSION = "1.4.2"
2
+ VERSION = "1.5.2"
3
3
  end
data/lib/mgmg.rb CHANGED
@@ -7,134 +7,237 @@ require_relative './mgmg/ir'
7
7
  require_relative './mgmg/system_equip'
8
8
  require_relative './mgmg/cuisine'
9
9
  require_relative './mgmg/reinforce'
10
+ require_relative './mgmg/option'
11
+ require_relative './mgmg/recipe'
10
12
  require_relative './mgmg/search'
11
13
  require_relative './mgmg/optimize'
12
14
 
13
15
  class String
14
- def min_level(w=1)
15
- Mgmg::Equip.min_level(self, w)
16
+ using Mgmg::Refiner
17
+ def to_recipe(para=:power, allow_over20: false, **kw)
18
+ ret = Mgmg::Recipe.new(self, para, **kw)
19
+ raise Mgmg::Over20Error, ret.ir.star if (!allow_over20 and 20<ret.ir.star)
20
+ ret
21
+ end
22
+ def min_weight(opt: Mgmg::Option.new)
23
+ build(build(opt:).min_levels_max, opt:).weight
24
+ end
25
+ def max_weight(include_outsourcing=false, opt: Mgmg::Option.new)
26
+ if include_outsourcing
27
+ build(-1, opt:).weight
28
+ else
29
+ build(min_smith(opt:), opt:).weight
30
+ end
31
+ end
32
+ def min_level(w=0, include_outsourcing=false, opt: Mgmg::Option.new)
33
+ built = build(-1, opt:)
34
+ w = build(built.min_levels_max, -1, opt:).weight - w if w <= 0
35
+ return -1 if include_outsourcing && built.weight <= w
36
+ ms = min_smith(opt:)
37
+ return ms if build(ms, opt:).weight <= w
38
+ ary = [ms]
39
+ 4.downto(1) do |wi| # 単品の最大重量は[斧|重鎧](金10石10)の5
40
+ built.min_levels(wi).values.each do |v|
41
+ (ary.include?(v) or ary << v) if ms < v
42
+ end
43
+ end
44
+ ary.sort.each do |l|
45
+ return l if build(l, opt:).weight <= w
46
+ end
47
+ raise ArgumentError, "w=`#{w}' is given, but the minimum weight for the recipe is `#{min_weight(opt:)}'."
48
+ end
49
+ def min_levels(w=1, opt: Mgmg::Option.new)
50
+ build(opt:).min_levels(w)
16
51
  end
17
- def min_levels(w=1, left_associative: true)
18
- build(-1, -1, left_associative: left_associative).min_levels(w)
52
+ def min_levels_max(w=1, opt: Mgmg::Option.new)
53
+ min_levels(w, opt:).values.append(-1).max
19
54
  end
20
- def min_smith(left_associative: true)
21
- Mgmg::Equip.min_smith(self, left_associative: left_associative)
55
+ def min_smith(opt: Mgmg::Option.new)
56
+ Mgmg::Equip.min_smith(self, opt:)
22
57
  end
23
- def min_comp(left_associative: true)
24
- Mgmg::Equip.min_comp(self, left_associative: left_associative)
58
+ def min_comp(opt: Mgmg::Option.new)
59
+ Mgmg::Equip.min_comp(self, opt:)
25
60
  end
26
- def build(smith=-1, comp=smith, left_associative: true)
27
- Mgmg::Equip.build(self, smith, comp, left_associative: left_associative)
61
+ def build(smith=-1, comp=smith, opt: Mgmg::Option.new)
62
+ Mgmg::Equip.build(self, smith, comp, left_associative: opt.left_associative).reinforce(*opt.reinforcement)
28
63
  end
29
- def ir(left_associative: true, reinforcement: [])
30
- Mgmg::IR.build(self, left_associative: left_associative, reinforcement: reinforcement)
64
+ def ir(opt: Mgmg::Option.new)
65
+ Mgmg::IR.build(self, left_associative: opt.left_associative, reinforcement: opt.reinforcement)
31
66
  end
32
- def poly(para=:cost, left_associative: true)
33
- la = left_associative
67
+ def poly(para=:cost, opt: Mgmg::Option.new)
34
68
  case para
35
69
  when :atkstr
36
- self.poly(:attack, left_associative: la) + self.poly(:str, left_associative: la)
70
+ self.poly(:attack, opt:) + self.poly(:str, opt:)
37
71
  when :atk_sd
38
- self.poly(:attack, left_associative: la) + self.poly(:str, left_associative: la).quo(2) + self.poly(:dex, left_associative: la).quo(2)
72
+ self.poly(:attack, opt:) + self.poly(:str, opt:).quo(2) + self.poly(:dex, opt:).quo(2)
39
73
  when :dex_as
40
- self.poly(:dex, left_associative: la) + self.poly(:attack, left_associative: la).quo(2) + self.poly(:str, left_associative: la).quo(2)
74
+ self.poly(:dex, opt:) + self.poly(:attack, opt:).quo(2) + self.poly(:str, opt:).quo(2)
41
75
  when :mag_das
42
- self.poly(:magic, left_associative: la) + self.poly(:dex_as, left_associative: la).quo(2)
76
+ self.poly(:magic, opt:) + self.poly(:dex_as, opt:).quo(2)
77
+ when :magic2
78
+ self.poly(:magic, opt:).scalar(2)
43
79
  when :magmag
44
- self.poly(:magdef, left_associative: la) + self.poly(:magic, left_associative: la).quo(2)
80
+ self.poly(:magdef, opt:) + self.poly(:magic, opt:).quo(2)
45
81
  when :pmdef
46
- pd = self.poly(:phydef, left_associative: la)
47
- md = self.poly(:magmag, left_associative: la)
82
+ pd = self.poly(:phydef, opt:)
83
+ md = self.poly(:magmag, opt:)
48
84
  pd <= md ? pd : md
49
85
  when :cost
50
- if Mgmg::SystemEquip.keys.include?(self)
86
+ if Mgmg::SystemEquip.has_key?(self)
51
87
  return Mgmg::TPolynomial.new(Mgmg::Mat.new(1, 1, 0.quo(1)), 28, 0, 12, 12)
52
88
  end
53
- built = self.build(-1)
89
+ built = self.build(-1, opt:)
54
90
  const = (built.star**2) * ( /\+/.match(self) ? 5 : ( built.kind < 8 ? 2 : 1 ) )
55
- ret = poly(:attack, left_associative: la) + poly(:phydef, left_associative: la) + poly(:magdef, left_associative: la)
56
- ret += poly(:hp, left_associative: la).quo(4) + poly(:mp, left_associative: la).quo(4)
57
- ret += poly(:str, left_associative: la) + poly(:dex, left_associative: la) + poly(:speed, left_associative: la) + poly(:magic, left_associative: la)
91
+ ret = poly(:attack, opt:) + poly(:phydef, opt:) + poly(:magdef, opt:)
92
+ ret += poly(:hp, opt:).quo(4) + poly(:mp, opt:).quo(4)
93
+ ret += poly(:str, opt:) + poly(:dex, opt:) + poly(:speed, opt:) + poly(:magic, opt:)
58
94
  ret.mat.body[0][0] += const
59
95
  ret
60
96
  else
61
- Mgmg::TPolynomial.build(self, para, left_associative: la)
97
+ Mgmg::TPolynomial.build(self, para, left_associative: opt.left_associative)
62
98
  end
63
99
  end
64
- def eff(para, smith, comp=smith, left_associative: true)
65
- a = build(smith, comp, left_associative: left_associative).para_call(para)
66
- b = build(smith+1, comp, left_associative: left_associative).para_call(para)
67
- c = build(smith, comp+2, left_associative: left_associative).para_call(para)
100
+ def eff(para, smith, comp=smith, opt: Mgmg::Option.new)
101
+ a = build(smith, comp, opt:).para_call(para)
102
+ b = build(smith+1, comp, opt:).para_call(para)
103
+ c = build(smith, comp+2, opt:).para_call(para)
68
104
  sden = smith==0 ? 1 : 2*smith-1
69
105
  cden = comp==0 ? 4 : 8*comp
70
106
  [(b-a).quo(sden), (c-a).quo(cden)]
71
107
  end
72
- def peff(para, smith, comp=smith, left_associative: true)
73
- poly(para, left_associative: left_associative).eff(smith, comp)
108
+ def peff(para, smith, comp=smith, opt: Mgmg::Option.new)
109
+ poly(para, opt:).eff(smith, comp)
74
110
  end
75
- def show(smith=-1, comp=smith, left_associative: true, para: :power, reinforcement: [])
76
- rein = case reinforcement
111
+ def show(smith=-1, comp=smith, para: :power, opt: Mgmg::Option.new)
112
+ rein = case opt.reinforcement
77
113
  when Array
78
- reinforcement.map{|r| Mgmg::Reinforcement.compile(r)}
114
+ opt.reinforcement.map{|r| Mgmg::Reinforcement.compile(r)}
79
115
  else
80
- [Mgmg::Reinforcement.compile(reinforcement)]
116
+ [Mgmg::Reinforcement.compile(opt.reinforcement)]
81
117
  end
82
- built = self.build(smith, comp, left_associative: left_associative).reinforce(*rein)
118
+ built = build(smith, comp, opt:)
83
119
  pstr = '%.3f' % built.para_call(para)
84
120
  pstr.sub!(/\.?0+\Z/, '')
85
- puts "Building"
121
+ puts "With levels (#{smith}, #{comp}: #{Mgmg.exp(smith, comp).comma3}), building"
86
122
  puts " #{self}"
87
- rein = rein.empty? ? '' : " reinforced by {#{rein.join(',')}}"
88
- puts "with levels (#{smith}, #{comp})#{rein} yields (#{pstr}, #{built.total_cost})"
123
+ rein = rein.empty? ? '' : "reinforced by {#{rein.join(',')}} "
124
+ puts "#{rein}yields (#{pstr}, #{built.total_cost})"
89
125
  puts " #{built}"
90
126
  end
91
- def phydef_optimize(smith=nil, comp=smith, left_associative: true, magdef_maximize: true)
92
- Mgmg::Optimize.phydef_optimize(self, smith, comp, left_associative: left_associative, magdef_maximize: magdef_maximize)
127
+ def phydef_optimize(smith=nil, comp=smith, opt: Mgmg::Option.new)
128
+ Mgmg::Optimize.phydef_optimize(self, smith, comp, opt:)
93
129
  end
94
- def buster_optimize(smith=nil, comp=smith, left_associative: true)
95
- Mgmg::Optimize.buster_optimize(self, smith, comp, left_associative: left_associative)
130
+ def buster_optimize(smith=nil, comp=smith, opt: Mgmg::Option.new)
131
+ Mgmg::Optimize.buster_optimize(self, smith, comp, opt:)
96
132
  end
97
133
  end
98
134
  module Enumerable
99
- def build(smith=-1, armor=smith, comp=armor.tap{armor=smith}, left_associative: true)
100
- self.sum do |str|
101
- m = /\A\[*([^\+]+)/.match(str)
102
- if Mgmg::EquipPosition[m[1].build(0).kind] == 0
103
- str.build(smith, comp, left_associative: left_associative)
135
+ using Mgmg::Refiner
136
+ def to_recipe(para=:power, **kw)
137
+ Mgmg::Recipe.new(self, para, **kw)
138
+ end
139
+ def build(smith=-1, armor=smith, comp=armor.tap{armor=smith}, opt: Mgmg::Option.new)
140
+ opt = opt.dup
141
+ rein = opt.reinforcement
142
+ opt.reinforcement = []
143
+ self.sum(Mgmg::Equip::Zero) do |str|
144
+ if Mgmg::EquipPosition[str.build(opt:).kind] == 0
145
+ str.build(smith, comp, opt:)
104
146
  else
105
- str.build(armor, comp, left_associative: left_associative)
147
+ str.build(armor, comp, opt:)
106
148
  end
107
- end
149
+ end.reinforce(*rein)
108
150
  end
109
- def ir(left_associative: true, reinforcement: [])
110
- self.sum do |str|
111
- str.ir(left_associative: left_associative)
112
- end.add_reinforcement(reinforcement)
151
+ def ir(opt: Mgmg::Option.new)
152
+ self.sum(Mgmg::IR::Zero) do |str|
153
+ str.ir(opt:)
154
+ end.add_reinforcement(opt.reinforcement)
113
155
  end
114
- def show(smith=-1, armor=smith, comp=armor.tap{armor=smith}, left_associative: true, para: :power, reinforcement: [])
115
- rein = case reinforcement
156
+ def show(smith=-1, armor=smith, comp=armor.tap{armor=smith}, para: :power, opt: Mgmg::Option.new)
157
+ rein = case opt.reinforcement
116
158
  when Array
117
- reinforcement.map{|r| Mgmg::Reinforcement.compile(r)}
159
+ opt.reinforcement.map{|r| Mgmg::Reinforcement.compile(r)}
118
160
  else
119
- [Mgmg::Reinforcement.compile(reinforcement)]
161
+ [Mgmg::Reinforcement.compile(opt.reinforcement)]
120
162
  end
121
- built = self.build(smith, armor, comp, left_associative: left_associative).reinforce(*rein)
163
+ built = self.build(smith, armor, comp, opt:)
122
164
  pstr = '%.3f' % built.para_call(para)
123
165
  pstr.sub!(/\.?0+\Z/, '')
124
- puts "Building"
166
+ puts "With levels (#{smith}, #{armor}, #{comp}: #{Mgmg.exp(smith, armor, comp).comma3}), building"
125
167
  puts " #{self.join(', ')}"
126
- rein = rein.empty? ? '' : " reinforced by {#{rein.join(',')}}"
127
- puts "with levels (#{smith}, #{armor}, #{comp})#{rein} yields (#{pstr}, #{built.total_cost})"
168
+ rein = rein.empty? ? '' : "reinforced by {#{rein.join(',')}} "
169
+ puts "#{rein}yields (#{pstr}, #{built.total_cost})"
128
170
  puts " #{built}"
129
171
  end
130
- def min_levels(w=1, left_associative: true)
131
- build(-1, -1, -1, left_associative: left_associative).min_levels(w)
172
+ def min_weight(opt: Mgmg::Option.new)
173
+ build(*build(opt:).min_levels_max, -1, opt:).weight
174
+ end
175
+ def max_weight(include_outsourcing=false, opt: Mgmg::Option.new)
176
+ if include_outsourcing
177
+ build(-1, opt:).weight
178
+ else
179
+ build(*min_smith(opt:), -1, opt:).weight
180
+ end
181
+ end
182
+ def min_weights(opt: Mgmg::Option.new)
183
+ weapons, armors = [], []
184
+ each do |str|
185
+ if Mgmg::EquipPosition[str.build(opt:).kind] == 0
186
+ weapons << str
187
+ else
188
+ armors << str
189
+ end
190
+ end
191
+ [weapons.min_weight(opt:), armors.min_weight(opt:)]
192
+ end
193
+ def max_weights(include_outsourcing=false, opt: Mgmg::Option.new)
194
+ weapons, armors = [], []
195
+ each do |str|
196
+ if Mgmg::EquipPosition[str.build(opt:).kind] == 0
197
+ weapons << str
198
+ else
199
+ armors << str
200
+ end
201
+ end
202
+ [weapons.max_weight(include_outsourcing, opt:), armors.max_weight(include_outsourcing, opt:)]
203
+ end
204
+ def min_level(ws=0, wa=ws, include_outsourcing=false, opt: Mgmg::Option.new)
205
+ weapons, armors = [], []
206
+ each do |str|
207
+ if Mgmg::EquipPosition[str.build(opt:).kind] == 0
208
+ weapons << str
209
+ else
210
+ armors << str
211
+ end
212
+ end
213
+ ms, ma = min_smith(opt:)
214
+ rs = min_level_sub(ws, ms, 0, weapons, include_outsourcing, opt:)
215
+ ra = min_level_sub(wa, ma, 1, armors, include_outsourcing, opt:)
216
+ [rs, ra]
217
+ end
218
+ private def min_level_sub(w, ms, i, recipe, include_outsourcing, opt: Mgmg::Option.new)
219
+ built = recipe.build(opt:)
220
+ w = recipe.build(built.min_levels_max[i], opt:).weight - w if w <= 0
221
+ return -1 if include_outsourcing && built.weight <= w
222
+ return ms if build(ms, opt:).weight <= w
223
+ ary = [ms]
224
+ 4.downto(1) do |wi|
225
+ built.min_levels(wi).values.each do |v|
226
+ (ary.include?(v) or ary << v) if ms << v
227
+ end
228
+ end
229
+ ary.sort.each do |l|
230
+ return l if recipe.build(l, opt:).weight <= w
231
+ end
232
+ raise ArgumentError, "w#{%w|s a|[i]}=`#{w}' is given, but the minimum weight for the #{%w|weapon(s) armor(s)|[i]} is `#{recipe.min_weight(opt:)}'."
233
+ end
234
+ def min_levels(w=1, opt: Mgmg::Option.new)
235
+ build(opt:).min_levels(w)
132
236
  end
133
- def min_level(w=1, left_associative: true)
134
- ret = [0, 0]
135
- build(-1, -1, -1, left_associative: left_associative).min_levels(w).each do |str, level|
136
- m = /\A\[*([^\+]+)/.match(str)
137
- if Mgmg::EquipPosition[m[1].build(0).kind] == 0
237
+ def min_levels_max(w=1, opt: Mgmg::Option.new)
238
+ ret = [-1, -1]
239
+ min_levels(w, opt:).each do |str, level|
240
+ if Mgmg::EquipPosition[str.build(opt:).kind] == 0
138
241
  ret[0] = [ret[0], level].max
139
242
  else
140
243
  ret[1] = [ret[1], level].max
@@ -142,12 +245,11 @@ module Enumerable
142
245
  end
143
246
  ret
144
247
  end
145
- def min_smith(left_associative: true)
146
- ret = [0, 0]
248
+ def min_smith(opt: Mgmg::Option.new)
249
+ ret = [-1, -1]
147
250
  self.each do |str|
148
- s = Mgmg::Equip.min_smith(str, left_associative: left_associative)
149
- m = /\A\[*([^\+]+)/.match(str)
150
- if Mgmg::EquipPosition[m[1].build(0).kind] == 0
251
+ s = Mgmg::Equip.min_smith(str, opt:)
252
+ if Mgmg::EquipPosition[str.build(opt:).kind] == 0
151
253
  ret[0] = [ret[0], s].max
152
254
  else
153
255
  ret[1] = [ret[1], s].max
@@ -155,9 +257,9 @@ module Enumerable
155
257
  end
156
258
  ret
157
259
  end
158
- def min_comp(left_associative: true)
260
+ def min_comp(opt: Mgmg::Option.new)
159
261
  self.map do |str|
160
- Mgmg::Equip.min_comp(str, left_associative: left_associative)
161
- end.max
262
+ Mgmg::Equip.min_comp(str, opt:)
263
+ end.append(-1).max
162
264
  end
163
265
  end
data/mgmg.gemspec CHANGED
@@ -35,9 +35,9 @@ Gem::Specification.new do |spec|
35
35
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
36
36
  spec.require_paths = ["lib"]
37
37
 
38
- spec.add_development_dependency "bundler", ">= 2.1.2"
39
- spec.add_development_dependency "rake", ">= 13.0.1"
40
- spec.add_development_dependency "irb", ">= 1.2.3"
38
+ spec.add_development_dependency "bundler", ">= 2.3.16"
39
+ spec.add_development_dependency "rake", ">= 13.0.6"
40
+ spec.add_development_dependency "irb", ">= 1.4.1"
41
41
 
42
- spec.required_ruby_version = '>= 2.4.0'
42
+ spec.required_ruby_version = '>= 3.1.0'
43
43
  end