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