mgmg 1.4.2 → 1.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 32ba0853f1b7578105ed06d7ebe4286544ae2a3188a2ab58c9d577b0e61bdd09
4
- data.tar.gz: de86373a23cd69a9cbed9a9f28ce2fe0e10ef3dee1b7803a2eaeac154fda0cec
3
+ metadata.gz: '0939daf2680e3d9a8a80a9616355ed10232ce6794b288ea86d295d283cfde413'
4
+ data.tar.gz: 7c6f9fdf112335d9844f1ec9e06c694b9cffd0a897787b4c9e74b306296b179c
5
5
  SHA512:
6
- metadata.gz: 123076c3d2728af73cf53b1dbc9834d0eea262fcab89a454618b2e63798156c98d127a54dbb40e696af122064ad435a7794af165dc96a4ca883cf1a2c8256814
7
- data.tar.gz: 79fe5ebb67a840611930042b9f75c21839d79d1b3164f59b88a88db66a4fdba719dda819414ecaa1fcf379507426f6776b99058f2329662b2838d001a94f0320
6
+ metadata.gz: 31c5e140183a5417c833b7af756b454701333d71293bad1e5cd3f15073d71657c0503038daf0f7c7c02290aee22cef38c795ff98fc92df85462da96759c816f9
7
+ data.tar.gz: d02cfe4e1f571b76083c644cd0c44d41caced733e22d4c24853ac91b2a3410f53500c7867bda2b2109d46a873d4110a801d1cf2ecb7f2a3a54ba48f1d31d8b51
data/CHANGELOG.md CHANGED
@@ -120,3 +120,14 @@
120
120
  - `Mgmg::Equip#reinforce`および`Mgmg::IR`を使うメソッド群に`reinforcement`キーワード引数を追加.
121
121
  - スキルおよび料理による強化効果をシミュレートできるようになった.
122
122
  - 料理については,プリセット料理名または`Mgmg.#cuisine`で生成される`Mgmg::Cuisine`オブジェクトを使う.
123
+
124
+ ## 1.5.0 2022/06/22
125
+ - `Mgmg::Option`を実装し,search系メソッドを中心としたキーワード引数を,このクラスに集約してから受け渡すようにした.
126
+ - オプションオブジェクトは`Mgmg.#option`によって生成し,キーワード引数`opt`に渡す.従来のキーワード引数は廃止された.
127
+ - この変更で,一部の受け渡し忘れのバグが修正され,パフォーマンスが改善した.
128
+ - `Enumerable#search`で無駄な処理が発生していて,処理時間がかかっていたバグを修正.
129
+ - `String#min_level`,`Enumerable#min_level`の仕様を変更し,合成後の重量を目標値にするのに必要な鍛冶・道具製作Lvを計算するようにした.
130
+ - この変更に伴い,`Mgmg::Equip#min_level`を,`Mgmg::Equip#min_levels_max`に名称変更し,`String#min_levels_max`,`Enumerable#min_levels_max`を追加した.
131
+ - 関連して,`String#max_weight`,`String#min_weight`,`Enumerable#max_weight`,`Enumerable#min_weight`,`Enumerable#max_weights`,`Enumerable#min_weights`を追加した.
132
+ - `Mgmg::Option#smith_min`,`Mgmg::Option#armor_min`のデフォルト値を,`String#min_level`,`Enumerable#min_level`を用いて設定する仕様とし,その目標重量を`Mgmg::Option#target_weight`で指定するようにした.
133
+ - `String#min_comp`,`String#min_smith`,`Enumerable#min_comp`,`Enumerable#min_smith`において,既製品のみである,合成を行わないなど,該当スキルが必要ないレシピである場合の返り値を`-1`に変更した.
data/README.md CHANGED
@@ -53,19 +53,21 @@ puts r.build(122, 139, 232)
53
53
  #=> 複数装備9(武:1, 頭:1, 飾:2)[攻撃:15, 物防:34, 魔防:28, HP:241, MP:71, 器用:223, 素早:222, 魔力:6,604]
54
54
  ```
55
55
 
56
- 重量1または2で作るのに必要な防具製作Lvを確認する.
57
-
56
+ 製作品の重量及び特定の重量にするために必要な鍛冶・防具製作Lvを確認する.
58
57
  ```ruby
59
- p ['重鎧(皮1010)'.min_level, '重鎧(皮10金10)'.min_level(2)]
60
- #=> [162, 42]
61
- ```
62
-
63
- 合成レシピから必要製作Lvを確認する.
64
- ```ruby
65
- p '[杖(水玉10火玉5)+本(骨10鉄1)]+[本(水玉5綿2)+杖(骨10鉄1)]'.min_levels
66
- #=> {"杖(水玉10火玉5)"=>92, "本(骨10鉄1)"=>48, "本(水玉5綿2)"=>12, "杖(骨10鉄1)"=>28}
67
- p '[杖(水玉10火玉5)+本(骨10鉄1)]+[本(水玉5綿2)+杖(骨10鉄1)]'.build.min_level
68
- #=> 92
58
+ r = 'サンダル(骨2皮1)+[重鎧(金3金3)+[フード(骨2宝1)+[重鎧(皮10金10)+軽鎧(鉄10皮1)]]]'
59
+ p r.min_levels # 各材料装備を重量1で作るのに必要な防具製作Lv
60
+ #=> {"サンダル(骨2皮1)"=>3, "重鎧(金3金3)"=>202, "フード(骨2宝1)"=>3, "重鎧(皮10金10)"=>162, "軽鎧(鉄10皮1)"=>68}
61
+ p r.min_levels(2) # 同重量2以下
62
+ #=> {"サンダル(骨2皮1)"=>3, "重鎧(金3金3)"=>102, "フード(骨2宝1)"=>3, "重鎧(皮10金10)"=>42, "軽鎧(鉄10皮1)"=>27}
63
+ p [r.min_weight, r.max_weight] # 最小重量と最大重量
64
+ #=> [5, 11]
65
+ p r.min_level # 最小重量で作るのに必要な防具製作Lv
66
+ #=> 202
67
+ p r.min_level(7) # 重量7以下で作るのに必要な防具製作Lv
68
+ #=> 102
69
+ p r.min_level(-1) # 最小重量+1=重量6以下で作るのに必要な防具製作Lv
70
+ #=> 162
69
71
  ```
70
72
 
71
73
  近似多項式を得る.
@@ -91,11 +93,21 @@ p [sc, Mgmg.exp(*sc)]
91
93
  #=> [[155, 376], 304969]
92
94
  ```
93
95
 
96
+ 最小重量で製作できない鍛冶レベルを探索範囲に含める.
97
+
98
+ ```ruby
99
+ r = '双短剣(金3皮1)+[杖(水1綿1)+[斧(玉5水1)+[杖(鉄1綿1)+[[斧(木2金3)+剣(鉄10皮1)]+[剣(木2綿1)+双短剣(鉄10皮1)]]]]]'
100
+ sc = r.search(:atk_sd, 2000, opt: Mgmg.option(target_weight: 9))
101
+ p [sc, Mgmg.exp(*sc)]
102
+ #=> [[70, 130], 38046]
103
+ # オプションなしの場合は鍛冶Lv100以上を探索するため,[[100, 126], 41054] になる
104
+ ```
105
+
94
106
  探索の際,スキル及び料理を考慮する.
95
107
 
96
108
  ```ruby
97
109
  r = '重鎧(皮2綿1)+[帽子(宝1宝1)+[重鎧(玉5金3)+[帽子(宝1宝1)+[重鎧(玉5金6)+[軽鎧(金3骨1)+[重鎧(皮2骨1)+軽鎧(鉄10綿1)]]]]]]'
98
- sc = r.search(:phydef, 100_000, reinforcement: %w|物防御UP アースドランと氷河酒の蒸し焼き ガードアップ|)
110
+ sc = r.search(:phydef, 100_000, opt: Mgmg.option(buff: %w|物防御UP アースドランと氷河酒の蒸し焼き ガードアップ|))
99
111
  p [sc, Mgmg.exp(*sc)]
100
112
  #=> [[120, 264], 152502]
101
113
  ```
data/lib/mgmg/equip.rb CHANGED
@@ -57,15 +57,15 @@ module Mgmg
57
57
  @min_levels
58
58
  else
59
59
  @min_levels.map do |key, value|
60
- [key, key.min_level(w)]
60
+ [key, Equip.min_level(key, w)]
61
61
  end.to_h
62
62
  end
63
63
  end
64
- def min_level(w=1)
64
+ def min_levels_max(w=1)
65
65
  if @kind == 28
66
- ret = [0, 0]
66
+ ret = [-1, -1]
67
67
  min_levels(w).each do |str, ml|
68
- if str.build(-1).kind < 8
68
+ if str.build.kind < 8
69
69
  if ret[0] < ml
70
70
  ret[0] = ml
71
71
  end
@@ -77,7 +77,7 @@ module Mgmg
77
77
  end
78
78
  ret
79
79
  else
80
- min_levels(w).values.append(0).max
80
+ min_levels(w).values.append(-1).max
81
81
  end
82
82
  end
83
83
 
@@ -193,15 +193,9 @@ module Mgmg
193
193
  def +(other)
194
194
  self.dup.add!(other)
195
195
  end
196
- def coerce(other)
197
- if other == 0
198
- zero = self.class.new(28, 0, Vec.new(6, 0), 12, 12, Vec.new(9, 0), Vec.new(3, 0))
199
- zero.history.clear
200
- [zero, self]
201
- else
202
- raise TypeError, "Mgmg::Equip can't be coerced into other than 0"
203
- end
204
- end
196
+ Zero = self.new(28, 0, Vec.new(6, 0), 12, 12, Vec.new(9, 0), Vec.new(3, 0))
197
+ Zero.history.clear
198
+ Zero.freeze
205
199
  end
206
200
 
207
201
  class << Equip
@@ -212,7 +206,7 @@ module Mgmg
212
206
  end
213
207
  private def build_sub0(stack, str)
214
208
  SystemEquip.each do |k, v|
215
- if Regexp.compile(k).match(str)
209
+ if SystemEquipRegexp[k].match(str)
216
210
  stack << v
217
211
  str = str.gsub(k, "<#{stack.length-1}>")
218
212
  end
@@ -305,7 +299,7 @@ module Mgmg
305
299
 
306
300
  ret = new(kind, ( weight<1 ? 1 : weight ), (main_s+sub_s).div(2), main_mc, sub_mc, para, ele)
307
301
  ret.total_cost[kind < 8 ? 0 : 2] += ret.smith_cost(outsourcing)
308
- ret.min_levels.store(str, str.min_level)
302
+ ret.min_levels.store(str, Equip.min_level(str))
309
303
  ret
310
304
  end
311
305
 
@@ -323,14 +317,14 @@ module Mgmg
323
317
  [(main_s-1)*3, (sub_s-1)*3, l].max
324
318
  end
325
319
 
326
- def min_comp(str, left_associative: true)
320
+ def min_comp(str, opt: Option.new)
327
321
  str = Mgmg.check_string(str)
328
322
  stack, str = minc_sub0([], str)
329
- (minc_sub(stack, str, left_associative)[1]-1)*3
323
+ (minc_sub(stack, str, opt.left_associative)[1]-1)*3
330
324
  end
331
325
  private def minc_sub0(stack, str)
332
326
  SystemEquip.each do |k, v|
333
- if Regexp.compile(k).match(str)
327
+ if SystemEquipRegexp[k].match(str)
334
328
  stack << v.star
335
329
  str = str.gsub(k, "<#{stack.length-1}>")
336
330
  end
@@ -352,14 +346,15 @@ module Mgmg
352
346
  end
353
347
  end
354
348
 
355
- def min_smith(str, left_associative: true)
349
+ def min_smith(str, opt: Option.new)
356
350
  str = Mgmg.check_string(str)
357
351
  stack, str = mins_sub0([], str)
358
- (([mins_sub(stack, str, left_associative)]+stack).max-1)*3
352
+ ret = (([mins_sub(stack, str, opt.left_associative)]+stack).max-1)*3
353
+ ret < 0 ? -1 : ret
359
354
  end
360
355
  private def mins_sub0(stack, str)
361
356
  SystemEquip.each do |k, v|
362
- if Regexp.compile(k).match(str)
357
+ if SystemEquipRegexp[k].match(str)
363
358
  stack << 0
364
359
  str = str.gsub(k, "<#{stack.length-1}>")
365
360
  end
@@ -373,7 +368,7 @@ module Mgmg
373
368
  elsif m = ( lassoc ? /\A(.+)\+(.+?)\Z/ : /\A(.+?)\+(.+)\Z/ ).match(str)
374
369
  [mins_sub(stack, m[1], lassoc), mins_sub(stack, m[2], lassoc)].max
375
370
  elsif m = /\A\<(\d+)\>\Z/.match(str)
376
- 1
371
+ 0
377
372
  else
378
373
  mins_sub2(str)
379
374
  end
data/lib/mgmg/ir.rb CHANGED
@@ -31,7 +31,7 @@ module Mgmg
31
31
  ((s+@sub9)*@coef).div(@den)
32
32
  end
33
33
  def evaluate3(s, a, c)
34
- if sa==:a
34
+ if @sa==:a
35
35
  ((a+@sub9)*@coef).div(@den)
36
36
  else
37
37
  ((s+@sub9)*@coef).div(@den)
@@ -313,14 +313,7 @@ module Mgmg
313
313
  def +(other)
314
314
  self.dup.add!(other)
315
315
  end
316
- def coerce(other)
317
- if other == 0
318
- zero = self.class.new(28, Vec.new(6, 0), 12, 12, Array.new(9){Const.new(0)})
319
- [zero, self]
320
- else
321
- raise TypeError, "Mgmg::IR can't be coerced into other than 0"
322
- end
323
- end
316
+ Zero = self.new(28, Vec.new(6, 0), 12, 12, Array.new(9){Const.new(0)})
324
317
  end
325
318
  class << IR
326
319
  def build(str, left_associative: true, reinforcement: [])
@@ -330,7 +323,7 @@ module Mgmg
330
323
  end
331
324
  private def build_sub0(stack, str)
332
325
  SystemEquip.each do |k, v|
333
- if Regexp.compile(k).match(str)
326
+ if SystemEquipRegexp[k].match(str)
334
327
  stack << from_equip(v)
335
328
  str = str.gsub(k, "<#{stack.length-1}>")
336
329
  end
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, left_associative: true, magdef_maximize: true)
6
- best = ( smith.nil? ? [str, str.poly(:phydef), str.poly(:magdef), str.poly(:cost)] : [str, str.build(smith, comp)] )
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 = pd_better(best, ( smith.nil? ? [r, r.poly(:phydef), r.poly(:magdef), r.poly(:cost)] : [r, r.build(smith, comp)] ), magdef_maximize)
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 = pd_better(best, ( smith.nil? ? [r, r.poly(:phydef), r.poly(:magdef), r.poly(:cost)] : [r, r.build(smith, comp)] ), magdef_maximize)
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 "Unexpected Error"
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, left_associative: true)
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]
@@ -0,0 +1,85 @@
1
+ module Mgmg
2
+ module_function def option(recipe=nil, **kw)
3
+ ret = Option.new(**kw)
4
+ ret.set_default(recipe) unless recipe.nil?
5
+ ret
6
+ end
7
+ class Option
8
+ def initialize(
9
+ left_associative: true,
10
+ smith_min: nil, armor_min:nil, comp_min: nil, smith_max: 10000, armor_max: 10000, comp_max: 10000,
11
+ step: 1, magdef_maximize: true,
12
+ target_weight: 0, reinforcement: [], buff: nil,
13
+ irep: nil, cut_exp: Float::INFINITY
14
+ )
15
+ @left_associative = left_associative
16
+ @smith_min = smith_min
17
+ @armor_min = armor_min
18
+ @comp_min = comp_min
19
+ @smith_max = smith_max
20
+ @armor_max = armor_max
21
+ @comp_max = comp_max
22
+ @step = step
23
+ @magdef_maximize = magdef_maximize
24
+ @target_weight = target_weight
25
+ @reinforcement = reinforcement
26
+ unless buff.nil?
27
+ if @reinforcement.empty?
28
+ @reinforcement = buff
29
+ else
30
+ raise ArgumentError, "reinforcement and buff are exclusive"
31
+ end
32
+ end
33
+ @irep = irep
34
+ @cut_exp = cut_exp
35
+ end
36
+ attr_accessor :left_associative, :smith_min, :armor_min, :comp_min, :smith_max, :armor_max, :comp_max
37
+ attr_accessor :step, :magdef_maximize, :target_weight, :reinforcement, :irep, :cut_exp
38
+ def initialize_copy(other)
39
+ @left_associative = other.left_associative
40
+ @smith_min = other.smith_min
41
+ @armor_min = other.armor_min
42
+ @comp_min = other.comp_min
43
+ @smith_max = other.smith_max
44
+ @armor_max = other.armor_max
45
+ @comp_max = other.comp_max
46
+ @step = other.step
47
+ @magdef_maximize = other.magdef_maximize
48
+ @target_weight = other.target_weight
49
+ @reinforcement = other.reinforcement.dup
50
+ @irep = other.irep
51
+ @cut_exp = other.cut_exp
52
+ end
53
+ def set_default(recipe, force: false)
54
+ case recipe
55
+ when String
56
+ if @smith_min.nil? && @armor_min
57
+ @smith_min = @armor_min
58
+ end
59
+ if force || @smith_min.nil?
60
+ s = recipe.min_level(@target_weight, opt: self)
61
+ @smith_min = s if force || @smith_min.nil?
62
+ @armor_min = s if force || @armor_min.nil?
63
+ end
64
+ when Enumerable
65
+ if force || @smith_min.nil? || @armor_min.nil?
66
+ @target_weight = [@target_weight, @target_weight] if @target_weight.kind_of? Numeric
67
+ s, a = recipe.min_level(*@target_weight, opt: self)
68
+ @smith_min = s if force || @smith_min.nil?
69
+ @armor_min = a if force || @armor_min.nil?
70
+ end
71
+ else
72
+ raise ArgumentError, 'recipe should be String or Enumerable'
73
+ end
74
+ @comp_min = recipe.min_comp(opt: self) if force || @comp_min.nil?
75
+ @irep = recipe.ir(opt: self) if force || @irep.nil?
76
+ self
77
+ end
78
+ def buff
79
+ @reinforcement
80
+ end
81
+ def buff=(v)
82
+ @reinforcement = v
83
+ end
84
+ end
85
+ end
@@ -14,7 +14,7 @@ module Mgmg
14
14
  alias :inspect :to_s
15
15
  end
16
16
 
17
- # 攻 物 防 HP MP 腕 器 速 魔
17
+ # スキル名 攻 物 防 HP MP 腕 器 速 魔
18
18
  Skill = {
19
19
  '物防御UP' => Reinforcement.new( Vec[ 0, 10, 0, 0, 0, 0, 0, 0, 0] ),
20
20
  '魔防御UP' => Reinforcement.new( Vec[ 0, 0, 10, 0, 0, 0, 0, 0, 0] ),
@@ -42,9 +42,9 @@ module Mgmg
42
42
  if Skill.has_key?(arg)
43
43
  Skill[arg]
44
44
  elsif SystemCuisine.has_key?(arg)
45
- SystemCuisine[arg]
45
+ cuisine(SystemCuisine[arg])
46
46
  else
47
- raise ArgumentError, "Unknown skill or preset cuisine name `#{arg}' is given."
47
+ raise InvalidReinforcementNameError, arg
48
48
  end
49
49
  else
50
50
  raise ArgumentError, "The argument should be Mgmg::Cuisine or skill name String. (`#{arg}' is given)"