mgmg 1.5.0 → 1.5.3
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 +13 -0
- data/README.md +25 -6
- data/lib/mgmg/equip.rb +3 -3
- data/lib/mgmg/ir.rb +3 -2
- data/lib/mgmg/optimize.rb +9 -9
- data/lib/mgmg/option.rb +5 -6
- data/lib/mgmg/recipe.rb +172 -0
- data/lib/mgmg/search.rb +34 -12
- data/lib/mgmg/system_equip.rb +2 -4
- data/lib/mgmg/utils.rb +44 -8
- data/lib/mgmg/version.rb +1 -1
- data/lib/mgmg.rb +78 -65
- data/reference.md +59 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 38cdc3b73d921d0af024b6a4e52401c2539dd3e06a300116054e6f6b0dca03d7
|
4
|
+
data.tar.gz: 4235ea18224761cdeced8d9026b8d27c9690093fb1116a7eb4a36827f3ea3c3c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dac5f9bdd58018092822acba25a1f9e1c965ea6a1d3756b12903c081be4cf6761ba3eabb76326dc4d587f171ece4adc8d861d425368c3090a25c2c5faa036948
|
7
|
+
data.tar.gz: bcecf50fa423d798bf74da4f4968054c1200aa31f597750964fa0f34fb53439916bcc455d35f43a82a6e0701e902a4ecf527961eed758d7daacc67c622ccc93d
|
data/CHANGELOG.md
CHANGED
@@ -131,3 +131,16 @@
|
|
131
131
|
- 関連して,`String#max_weight`,`String#min_weight`,`Enumerable#max_weight`,`Enumerable#min_weight`,`Enumerable#max_weights`,`Enumerable#min_weights`を追加した.
|
132
132
|
- `Mgmg::Option#smith_min`,`Mgmg::Option#armor_min`のデフォルト値を,`String#min_level`,`Enumerable#min_level`を用いて設定する仕様とし,その目標重量を`Mgmg::Option#target_weight`で指定するようにした.
|
133
133
|
- `String#min_comp`,`String#min_smith`,`Enumerable#min_comp`,`Enumerable#min_smith`において,既製品のみである,合成を行わないなど,該当スキルが必要ないレシピである場合の返り値を`-1`に変更した.
|
134
|
+
|
135
|
+
## 1.5.1 2022/06/24
|
136
|
+
- `Mgmg::Recipe`を実装し,レシピ`String`または`Enumerable`と,注目パラメータ`Symbol`,オプション`Mgmg::Option`をセットで取り扱えるようにした.
|
137
|
+
- `String#to_recipe(para=:power, allow_over20: false, **kw)`または`Enumerable#to_recipe(para=:power, **kw)`で生成できる.
|
138
|
+
- `Mgmg::Recipe#build`,`Mgmg::Recipe#search`など`String`等に対する操作と同様にでき,注目パラメータとオプションは,`to_recipe`でセットしたものがデフォルトで使われるようになる.各メソッド呼び出し時にキーワード引数を与えることで,一時的に上書きすることもできる.
|
139
|
+
- `String#to_recipe`にのみ,☆20制限のチェック機構を導入した.
|
140
|
+
- 計算を繰り返した際,複数装備の装備数が増加していってしまうバグを修正した.
|
141
|
+
|
142
|
+
## 1.5.2 2022/06/28
|
143
|
+
- `String#poly`が`:magic2`に対応していなかったバグを修正.
|
144
|
+
|
145
|
+
## 1.5.3 2022/06/28
|
146
|
+
- `Recipe#option`がキーワード引数を受け取れなかったバグを修正.
|
data/README.md
CHANGED
@@ -54,6 +54,7 @@ puts r.build(122, 139, 232)
|
|
54
54
|
```
|
55
55
|
|
56
56
|
製作品の重量及び特定の重量にするために必要な鍛冶・防具製作Lvを確認する.
|
57
|
+
|
57
58
|
```ruby
|
58
59
|
r = 'サンダル(骨2皮1)+[重鎧(金3金3)+[フード(骨2宝1)+[重鎧(皮10金10)+軽鎧(鉄10皮1)]]]'
|
59
60
|
p r.min_levels # 各材料装備を重量1で作るのに必要な防具製作Lv
|
@@ -61,7 +62,7 @@ p r.min_levels # 各材料装備を重量1で作るのに必要
|
|
61
62
|
p r.min_levels(2) # 同重量2以下
|
62
63
|
#=> {"サンダル(骨2皮1)"=>3, "重鎧(金3金3)"=>102, "フード(骨2宝1)"=>3, "重鎧(皮10金10)"=>42, "軽鎧(鉄10皮1)"=>27}
|
63
64
|
p [r.min_weight, r.max_weight] # 最小重量と最大重量
|
64
|
-
#=> [5,
|
65
|
+
#=> [5, 10]
|
65
66
|
p r.min_level # 最小重量で作るのに必要な防具製作Lv
|
66
67
|
#=> 202
|
67
68
|
p r.min_level(7) # 重量7以下で作るのに必要な防具製作Lv
|
@@ -70,6 +71,20 @@ p r.min_level(-1) # 最小重量+1=重量6以下で作るのに必
|
|
70
71
|
#=> 162
|
71
72
|
```
|
72
73
|
|
74
|
+
注目パラメータやオプションパラメータを,レシピ文字列とセットにして取り回す.
|
75
|
+
```ruby
|
76
|
+
r = '[[斧(鉄10皮1)+剣(牙10皮1)]+剣(鉄10皮1)]+剣(鉄10皮1)'
|
77
|
+
r = r.to_recipe(:atkstr, target_weight: 5) # 注目パラメータを攻撃力+腕力,目標重量を5に設定
|
78
|
+
p [r.min_level, r.min_level(0)] # 必要な鍛冶Lvのデフォルトが目標重量ベースに
|
79
|
+
#=> [72, 168] # 鍛冶Lv72で重量5(目標)に,鍛冶Lv168で重量4(最小)になる
|
80
|
+
puts r.build(27, 42) # 変換前と同様に合成等もできる
|
81
|
+
#=> 斧8☆20(鉄鉄)[攻撃:1,112, 腕力:18] # 鍛冶Lv27だと重量8に
|
82
|
+
p r.para_call(27, 42) # 注目パラメータのみを計算
|
83
|
+
#=> 1130 # 1112+18=1130
|
84
|
+
puts r.build(nil) # 目標重量を達成する最小製作Lvで製作
|
85
|
+
#=> 斧5☆20(鉄鉄)[攻撃:1,436, 腕力:24] # 製作Lvを nil にすると,目標重量のための最小製作Lv (72, 42) で製作
|
86
|
+
```
|
87
|
+
|
73
88
|
近似多項式を得る.
|
74
89
|
|
75
90
|
```ruby
|
@@ -97,22 +112,26 @@ p [sc, Mgmg.exp(*sc)]
|
|
97
112
|
|
98
113
|
```ruby
|
99
114
|
r = '双短剣(金3皮1)+[杖(水1綿1)+[斧(玉5水1)+[杖(鉄1綿1)+[[斧(木2金3)+剣(鉄10皮1)]+[剣(木2綿1)+双短剣(鉄10皮1)]]]]]'
|
100
|
-
|
115
|
+
r = r.to_recipe(:atk_sd, target_weight: 9)
|
116
|
+
sc = r.search(2000) # to_recipe で注目パラメータを入れているので,ここでは不要
|
101
117
|
p [sc, Mgmg.exp(*sc)]
|
102
|
-
#=> [[70, 130], 38046]
|
103
|
-
# オプションなしの場合は鍛冶Lv100以上を探索するため,[[100, 126], 41054] になる
|
118
|
+
#=> [[70, 130], 38046] # オプションなしの場合は鍛冶Lv100以上を探索するため,[[100, 126], 41054] になる
|
104
119
|
```
|
105
120
|
|
106
121
|
探索の際,スキル及び料理を考慮する.
|
107
122
|
|
108
123
|
```ruby
|
109
124
|
r = '重鎧(皮2綿1)+[帽子(宝1宝1)+[重鎧(玉5金3)+[帽子(宝1宝1)+[重鎧(玉5金6)+[軽鎧(金3骨1)+[重鎧(皮2骨1)+軽鎧(鉄10綿1)]]]]]]'
|
110
|
-
|
125
|
+
r = r.to_recipe(:phydef, buff: %w|物防御UP アースドランと氷河酒の蒸し焼き ガードアップ|)
|
126
|
+
sc = r.search(100_000)
|
111
127
|
p [sc, Mgmg.exp(*sc)]
|
112
128
|
#=> [[120, 264], 152502]
|
129
|
+
puts r.build(*sc) # 実際に作って確認
|
130
|
+
#=> 重鎧8☆20(綿宝)[物防:100,396.0, 魔防:4]
|
131
|
+
puts r.build(*sc, buff: []) # 一時的に強化を解除して素の性能を確認
|
132
|
+
#=> 重鎧8☆20(綿宝)[物防:32,538, 魔防:4]
|
113
133
|
```
|
114
134
|
|
115
|
-
|
116
135
|
各メソッドの詳しい説明等は [リファレンス](./reference.md) を参照されたい.
|
117
136
|
|
118
137
|
### 表記ゆれについて
|
data/lib/mgmg/equip.rb
CHANGED
@@ -13,7 +13,7 @@ module Mgmg
|
|
13
13
|
def initialize_copy(other)
|
14
14
|
@kind = other.kind
|
15
15
|
@weight = other.weight
|
16
|
-
@star = other.star
|
16
|
+
@star = other.star.dup
|
17
17
|
@main = other.main
|
18
18
|
@sub = other.sub
|
19
19
|
@para = other.para.dup
|
@@ -193,8 +193,8 @@ module Mgmg
|
|
193
193
|
def +(other)
|
194
194
|
self.dup.add!(other)
|
195
195
|
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
|
196
|
+
Zero = self.new(28, 0, Vec.new(6, 0).freeze, 12, 12, Vec.new(9, 0).freeze, Vec.new(3, 0).freeze)
|
197
|
+
Zero.total_cost.freeze; Zero.history.clear.freeze
|
198
198
|
Zero.freeze
|
199
199
|
end
|
200
200
|
|
data/lib/mgmg/ir.rb
CHANGED
@@ -152,7 +152,7 @@ module Mgmg
|
|
152
152
|
attr_accessor :kind, :star, :main, :sub, :para, :rein
|
153
153
|
def initialize_copy(other)
|
154
154
|
@kind = other.kind
|
155
|
-
@star = other.star
|
155
|
+
@star = other.star.dup
|
156
156
|
@main = other.main
|
157
157
|
@sub = other.sub
|
158
158
|
@para = other.para.dup
|
@@ -313,7 +313,8 @@ module Mgmg
|
|
313
313
|
def +(other)
|
314
314
|
self.dup.add!(other)
|
315
315
|
end
|
316
|
-
Zero = self.new(28, Vec.new(6, 0), 12, 12, Array.new(9){Const.new(0)})
|
316
|
+
Zero = self.new(28, Vec.new(6, 0).freeze, 12, 12, Array.new(9){Const.new(0)}.freeze)
|
317
|
+
Zero.rein.freeze; Zero.freeze
|
317
318
|
end
|
318
319
|
class << IR
|
319
320
|
def build(str, left_associative: true, reinforcement: [])
|
data/lib/mgmg/optimize.rb
CHANGED
@@ -4,9 +4,9 @@ module Mgmg
|
|
4
4
|
InvList = [%w|帽子 フード サンダル|.freeze, %w|宝1 骨1 木1 木2 骨2|.freeze, %w|宝1 骨1 木1|.freeze].freeze
|
5
5
|
def phydef_optimize(str, smith, comp=smith, opt: Option.new)
|
6
6
|
best = if smith.nil? then
|
7
|
-
[str, str.poly(:phydef, opt:
|
7
|
+
[str, str.poly(:phydef, opt:), str.poly(:magdef, opt:), str.poly(:cost, opt:)]
|
8
8
|
else
|
9
|
-
[str, str.build(smith, comp, opt:
|
9
|
+
[str, str.build(smith, comp, opt:)]
|
10
10
|
end
|
11
11
|
str = Mgmg.check_string(str)
|
12
12
|
ai = 0
|
@@ -23,7 +23,7 @@ module Mgmg
|
|
23
23
|
m = /([^\+]*\([^\(]+[綿皮]1\))\]*\Z/.match(str)
|
24
24
|
if m
|
25
25
|
if smith
|
26
|
-
if m[1].sub(/綿1\)/, '皮1)').build(smith, opt:
|
26
|
+
if m[1].sub(/綿1\)/, '皮1)').build(smith, opt:).weight == m[1].sub(/皮1\)/, '綿1)').build(smith, opt:).weight
|
27
27
|
skin = true
|
28
28
|
end
|
29
29
|
else
|
@@ -40,9 +40,9 @@ module Mgmg
|
|
40
40
|
while b
|
41
41
|
r = pd_apply_idx(str, a, b)
|
42
42
|
best = if smith.nil? then
|
43
|
-
pd_better(best, [r, r.poly(:phydef, opt:
|
43
|
+
pd_better(best, [r, r.poly(:phydef, opt:), r.poly(:magdef, opt:), r.poly(:cost, opt:)], opt.magdef_maximize)
|
44
44
|
else
|
45
|
-
pd_better(best, [r, r.build(smith, comp, opt:
|
45
|
+
pd_better(best, [r, r.build(smith, comp, opt:)], opt.magdef_maximize)
|
46
46
|
end
|
47
47
|
b = pd_next_b(b)
|
48
48
|
end
|
@@ -58,9 +58,9 @@ module Mgmg
|
|
58
58
|
while b
|
59
59
|
r = pd_apply_idx(str, a, b)
|
60
60
|
best = if smith.nil? then
|
61
|
-
pd_better(best, [r, r.poly(:phydef, opt:
|
61
|
+
pd_better(best, [r, r.poly(:phydef, opt:), r.poly(:magdef, opt:), r.poly(:cost, opt:)], opt.magdef_maximize)
|
62
62
|
else
|
63
|
-
pd_better(best, [r, r.build(smith, comp, opt:
|
63
|
+
pd_better(best, [r, r.build(smith, comp, opt:)], opt.magdef_maximize)
|
64
64
|
end
|
65
65
|
b = pd_next_b(b)
|
66
66
|
end
|
@@ -159,7 +159,7 @@ module Mgmg
|
|
159
159
|
|
160
160
|
MwList = %w|綿 皮 骨 木 水|.freeze
|
161
161
|
def buster_optimize(str, smith, comp=smith, opt: Option.new)
|
162
|
-
best = ( smith.nil? ? [str, str.poly(:mag_das, opt:
|
162
|
+
best = ( smith.nil? ? [str, str.poly(:mag_das, opt:)] : [str, str.build(smith, comp, opt:)] )
|
163
163
|
str = Mgmg.check_string(str)
|
164
164
|
ai = -1
|
165
165
|
org = nil
|
@@ -174,7 +174,7 @@ module Mgmg
|
|
174
174
|
a = Array.new(ai){ [0, 0, 0] }
|
175
175
|
while a
|
176
176
|
r = bus_apply_idx(str, a)
|
177
|
-
best = bus_better(best, ( smith.nil? ? [r, r.poly(:mag_das, opt:
|
177
|
+
best = bus_better(best, ( smith.nil? ? [r, r.poly(:mag_das, opt:)] : [r, r.build(smith, comp, opt:)] ))
|
178
178
|
a = bus_next_a(a)
|
179
179
|
end
|
180
180
|
best[0]
|
data/lib/mgmg/option.rb
CHANGED
@@ -1,9 +1,4 @@
|
|
1
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
2
|
class Option
|
8
3
|
def initialize(
|
9
4
|
left_associative: true,
|
@@ -50,7 +45,7 @@ module Mgmg
|
|
50
45
|
@irep = other.irep
|
51
46
|
@cut_exp = other.cut_exp
|
52
47
|
end
|
53
|
-
def
|
48
|
+
def update_sa_min(recipe, force=true)
|
54
49
|
case recipe
|
55
50
|
when String
|
56
51
|
if @smith_min.nil? && @armor_min
|
@@ -71,6 +66,10 @@ module Mgmg
|
|
71
66
|
else
|
72
67
|
raise ArgumentError, 'recipe should be String or Enumerable'
|
73
68
|
end
|
69
|
+
self
|
70
|
+
end
|
71
|
+
def set_default(recipe, force: false)
|
72
|
+
update_sa_min(recipe, force)
|
74
73
|
@comp_min = recipe.min_comp(opt: self) if force || @comp_min.nil?
|
75
74
|
@irep = recipe.ir(opt: self) if force || @irep.nil?
|
76
75
|
self
|
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:)
|
74
|
+
when Enumerable
|
75
|
+
recipe.build(smith, armor, comp, 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:, opt:)
|
86
|
+
when Enumerable
|
87
|
+
recipe.show(smith, armor, comp, para:, 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:)
|
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:)
|
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:)
|
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:)
|
167
|
+
else
|
168
|
+
raise InvalidRecipeError, "Mgmg::Recipe#buster_optimize is available only for String recipes."
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
data/lib/mgmg/search.rb
CHANGED
@@ -48,11 +48,11 @@ class String
|
|
48
48
|
def search(para, target, opt: Mgmg::Option.new)
|
49
49
|
opt = opt.dup.set_default(self)
|
50
50
|
opt_nocut = opt.dup; opt_nocut.cut_exp = Float::INFINITY
|
51
|
-
opt.comp_min = comp_search(para, target, opt.smith_max, opt:
|
51
|
+
opt.comp_min = comp_search(para, target, opt.smith_max, opt:)
|
52
52
|
opt.smith_max = smith_search(para, target, opt.comp_min, opt: opt_nocut)
|
53
53
|
opt.smith_min = smith_search(para, target, opt.comp_max, opt: opt_nocut)
|
54
54
|
raise Mgmg::SearchCutException if opt.cut_exp < Mgmg.exp(opt.smith_min, opt.comp_min)
|
55
|
-
opt.comp_max = comp_search(para, target, opt.smith_min, opt:
|
55
|
+
opt.comp_max = comp_search(para, target, opt.smith_min, opt:)
|
56
56
|
ret = nil
|
57
57
|
exp = Mgmg.exp(opt.smith_min, opt.comp_max)
|
58
58
|
opt.cut_exp, ret = exp, [opt.smith_min, opt.comp_max] if exp < opt.cut_exp
|
@@ -60,7 +60,7 @@ class String
|
|
60
60
|
opt.cut_exp, ret = exp, [opt.smith_max, opt.comp_min] if exp < opt.cut_exp
|
61
61
|
(opt.comp_min+opt.step).step(opt.comp_max-1, opt.step) do |comp|
|
62
62
|
break if opt.cut_exp < Mgmg.exp(opt.smith_min, comp)
|
63
|
-
smith = smith_search(para, target, comp, opt:
|
63
|
+
smith = smith_search(para, target, comp, opt:)
|
64
64
|
exp = Mgmg.exp(smith, comp)
|
65
65
|
if exp < opt.cut_exp
|
66
66
|
opt.cut_exp, ret = exp, [smith, comp]
|
@@ -144,7 +144,7 @@ module Enumerable
|
|
144
144
|
opt.cut_exp, ret = exp2, [opt.smith_max, opt.armor_min] if exp2 < opt.cut_exp
|
145
145
|
(opt.armor_min+1).upto(opt.armor_max-1) do |armor|
|
146
146
|
break if opt.cut_exp < Mgmg.exp(opt.smith_min, armor, comp)
|
147
|
-
smith = smith_search(para, target, armor, comp, opt:
|
147
|
+
smith = smith_search(para, target, armor, comp, opt:)
|
148
148
|
exp = Mgmg.exp(smith, armor, comp)
|
149
149
|
if exp < opt.cut_exp
|
150
150
|
opt.cut_exp, ret = exp, [smith, armor]
|
@@ -158,7 +158,7 @@ module Enumerable
|
|
158
158
|
else
|
159
159
|
(opt.smith_min+1).upto(opt.smith_max-1) do |smith|
|
160
160
|
break if opt.cut_exp < Mgmg.exp(smith, opt.armor_min, comp)
|
161
|
-
armor = armor_search(para, target, smith, comp, opt:
|
161
|
+
armor = armor_search(para, target, smith, comp, opt:)
|
162
162
|
exp = Mgmg.exp(smith, armor, comp)
|
163
163
|
if exp < opt.cut_exp
|
164
164
|
opt.cut_exp, ret = exp, [smith, armor]
|
@@ -195,11 +195,11 @@ module Enumerable
|
|
195
195
|
end
|
196
196
|
def search(para, target, opt: Mgmg::Option.new)
|
197
197
|
opt = opt.dup.set_default(self)
|
198
|
-
opt.comp_min = comp_search(para, target, opt.smith_max, opt.armor_max, opt:
|
199
|
-
opt.smith_max, opt.armor_max = sa_search(para, target, opt.comp_min, opt:
|
200
|
-
opt.smith_min, opt.armor_min = sa_search(para, target, opt.comp_max, opt:
|
198
|
+
opt.comp_min = comp_search(para, target, opt.smith_max, opt.armor_max, opt:)
|
199
|
+
opt.smith_max, opt.armor_max = sa_search(para, target, opt.comp_min, opt:)
|
200
|
+
opt.smith_min, opt.armor_min = sa_search(para, target, opt.comp_max, opt:)
|
201
201
|
raise Mgmg::SearchCutException if opt.cut_exp < Mgmg.exp(opt.smith_min, opt.armor_min, opt.comp_min)
|
202
|
-
opt.comp_max = comp_search(para, target, opt.smith_min, opt.armor_min, opt:
|
202
|
+
opt.comp_max = comp_search(para, target, opt.smith_min, opt.armor_min, opt:)
|
203
203
|
ret = nil
|
204
204
|
exp = Mgmg.exp(opt.smith_min, opt.armor_min, opt.comp_max)
|
205
205
|
opt.cut_exp, ret = exp, [opt.smith_min, opt.armor_min,opt. comp_max] if exp < opt.cut_exp
|
@@ -207,7 +207,7 @@ module Enumerable
|
|
207
207
|
opt.cut_exp, ret = exp, [opt.smith_max, opt.armor_max, opt.comp_min] if exp < opt.cut_exp
|
208
208
|
(opt.comp_min+1).upto(opt.comp_max-1) do |comp|
|
209
209
|
break if opt.cut_exp < Mgmg.exp(opt.smith_min, opt.armor_min, comp)
|
210
|
-
smith, armor = sa_search(para, target, comp, opt:
|
210
|
+
smith, armor = sa_search(para, target, comp, opt:)
|
211
211
|
exp = Mgmg.exp(smith, armor, comp)
|
212
212
|
if exp < opt.cut_exp
|
213
213
|
opt.cut_exp, ret = exp, [smith, armor, comp]
|
@@ -228,7 +228,18 @@ module Mgmg
|
|
228
228
|
if term <= start
|
229
229
|
raise ArgumentError, "start < term is needed, (start, term) = (#{start}, #{term}) are given"
|
230
230
|
end
|
231
|
-
|
231
|
+
if a.kind_of?(Recipe)
|
232
|
+
opt_a = a.option.dup
|
233
|
+
a = a.recipe
|
234
|
+
else
|
235
|
+
opt_a = opt_a.dup.set_default(a)
|
236
|
+
end
|
237
|
+
if b.kind_of?(Recipe)
|
238
|
+
opt_b = b.option.dup
|
239
|
+
b = b.recipe
|
240
|
+
else
|
241
|
+
opt_b = opt_b.dup.set_default(b)
|
242
|
+
end
|
232
243
|
sca, scb = a.search(para, start, opt: opt_a), b.search(para, start, opt: opt_b)
|
233
244
|
ea, eb = Mgmg.exp(*sca), Mgmg.exp(*scb)
|
234
245
|
if eb < ea || ( ea == eb && opt_a.irep.para_call(para, *sca) < opt_b.irep.para_call(para, *scb) )
|
@@ -263,7 +274,18 @@ module Mgmg
|
|
263
274
|
if start <= term
|
264
275
|
raise ArgumentError, "term < start is needed, (start, term) = (#{start}, #{term}) are given"
|
265
276
|
end
|
266
|
-
|
277
|
+
if a.kind_of?(Recipe)
|
278
|
+
opt_a = a.option.dup
|
279
|
+
a = a.recipe
|
280
|
+
else
|
281
|
+
opt_a = opt_a.dup.set_default(a)
|
282
|
+
end
|
283
|
+
if b.kind_of?(Recipe)
|
284
|
+
opt_b = b.option.dup
|
285
|
+
b = b.recipe
|
286
|
+
else
|
287
|
+
opt_b = opt_b.dup.set_default(b)
|
288
|
+
end
|
267
289
|
sca, scb = a.search(para, start, opt: opt_a), b.search(para, start, opt: opt_b)
|
268
290
|
ea, eb = Mgmg.exp(*sca), Mgmg.exp(*scb)
|
269
291
|
if ea < eb || ( ea == eb && opt_b.irep.para_call(para, *scb) < opt_a.irep.para_call(para, *sca) )
|
data/lib/mgmg/system_equip.rb
CHANGED
@@ -323,10 +323,8 @@ module Mgmg
|
|
323
323
|
SystemEquip.store(k.sub(/脛当て\Z/, 'すね当て'), SystemEquip[k])
|
324
324
|
end
|
325
325
|
end
|
326
|
-
SystemEquip.freeze
|
327
326
|
SystemEquipRegexp = Hash.new
|
328
|
-
SystemEquip.
|
329
|
-
SystemEquipRegexp.store(k
|
327
|
+
SystemEquip.each_key do |k|
|
328
|
+
SystemEquipRegexp.store(k, Regexp.compile(k).freeze)
|
330
329
|
end
|
331
|
-
SystemEquipRegexp.freeze
|
332
330
|
end
|
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
|
-
|
22
|
-
|
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
|
-
|
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
|
@@ -78,6 +95,20 @@ module Mgmg
|
|
78
95
|
end
|
79
96
|
attr_accessor :name
|
80
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
|
81
112
|
class SearchCutException < StandardError; end
|
82
113
|
class UnexpectedError < StandardError
|
83
114
|
def initialize()
|
@@ -122,6 +153,11 @@ module Mgmg
|
|
122
153
|
module_function def invexp3(exp, sa, comp)
|
123
154
|
Math.sqrt(exp - ((sa-1)**2) - (2*((comp-1)**2)) - 4).round + 1
|
124
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
|
125
161
|
|
126
162
|
CharacterList = /[^\(\)\+0123456789\[\]あきくしすたてなねのびりるイウガクグサジスタダチツデトドニノフブペボムラリルロンヴー一万二光兜典刀剣劣匠双古名吹咆品哮地大天太子安宝小帽弓弩当息悪戦手指斧書服木本杖業樹歴殺水氷法火炎牙物玉王産用界異的皮盾短石砕竜紫綿耳聖脛腕腿般良色衣袋覇質軍軽輝輪重量金鉄鎧闇陽靴額飾首骨鬼龍]/.freeze
|
127
163
|
module_function def check_string(str)
|
data/lib/mgmg/version.rb
CHANGED
data/lib/mgmg.rb
CHANGED
@@ -8,26 +8,33 @@ require_relative './mgmg/system_equip'
|
|
8
8
|
require_relative './mgmg/cuisine'
|
9
9
|
require_relative './mgmg/reinforce'
|
10
10
|
require_relative './mgmg/option'
|
11
|
+
require_relative './mgmg/recipe'
|
11
12
|
require_relative './mgmg/search'
|
12
13
|
require_relative './mgmg/optimize'
|
13
14
|
|
14
15
|
class String
|
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
|
15
22
|
def min_weight(opt: Mgmg::Option.new)
|
16
|
-
build(build(opt:
|
23
|
+
build(build(opt:).min_levels_max, opt:).weight
|
17
24
|
end
|
18
25
|
def max_weight(include_outsourcing=false, opt: Mgmg::Option.new)
|
19
26
|
if include_outsourcing
|
20
|
-
build(-1, opt:
|
27
|
+
build(-1, opt:).weight
|
21
28
|
else
|
22
|
-
build(min_smith(opt:
|
29
|
+
build(min_smith(opt:), opt:).weight
|
23
30
|
end
|
24
31
|
end
|
25
32
|
def min_level(w=0, include_outsourcing=false, opt: Mgmg::Option.new)
|
26
|
-
built = build(-1, opt:
|
27
|
-
w = build(built.min_levels_max, -1, opt:
|
33
|
+
built = build(-1, opt:)
|
34
|
+
w = build(built.min_levels_max, -1, opt:).weight - w if w <= 0
|
28
35
|
return -1 if include_outsourcing && built.weight <= w
|
29
|
-
ms = min_smith(opt:
|
30
|
-
return ms if build(ms, opt:
|
36
|
+
ms = min_smith(opt:)
|
37
|
+
return ms if build(ms, opt:).weight <= w
|
31
38
|
ary = [ms]
|
32
39
|
4.downto(1) do |wi| # 単品の最大重量は[斧|重鎧](金10石10)の5
|
33
40
|
built.min_levels(wi).values.each do |v|
|
@@ -35,21 +42,21 @@ class String
|
|
35
42
|
end
|
36
43
|
end
|
37
44
|
ary.sort.each do |l|
|
38
|
-
return l if build(l, opt:
|
45
|
+
return l if build(l, opt:).weight <= w
|
39
46
|
end
|
40
|
-
raise ArgumentError, "w=`#{w}' is given, but the minimum weight for the recipe is `#{min_weight(opt:
|
47
|
+
raise ArgumentError, "w=`#{w}' is given, but the minimum weight for the recipe is `#{min_weight(opt:)}'."
|
41
48
|
end
|
42
49
|
def min_levels(w=1, opt: Mgmg::Option.new)
|
43
|
-
build(opt:
|
50
|
+
build(opt:).min_levels(w)
|
44
51
|
end
|
45
52
|
def min_levels_max(w=1, opt: Mgmg::Option.new)
|
46
|
-
min_levels(w, opt:
|
53
|
+
min_levels(w, opt:).values.append(-1).max
|
47
54
|
end
|
48
55
|
def min_smith(opt: Mgmg::Option.new)
|
49
|
-
Mgmg::Equip.min_smith(self, opt:
|
56
|
+
Mgmg::Equip.min_smith(self, opt:)
|
50
57
|
end
|
51
58
|
def min_comp(opt: Mgmg::Option.new)
|
52
|
-
Mgmg::Equip.min_comp(self, opt:
|
59
|
+
Mgmg::Equip.min_comp(self, opt:)
|
53
60
|
end
|
54
61
|
def build(smith=-1, comp=smith, opt: Mgmg::Option.new)
|
55
62
|
Mgmg::Equip.build(self, smith, comp, left_associative: opt.left_associative).reinforce(*opt.reinforcement)
|
@@ -60,28 +67,30 @@ class String
|
|
60
67
|
def poly(para=:cost, opt: Mgmg::Option.new)
|
61
68
|
case para
|
62
69
|
when :atkstr
|
63
|
-
self.poly(:attack, opt:
|
70
|
+
self.poly(:attack, opt:) + self.poly(:str, opt:)
|
64
71
|
when :atk_sd
|
65
|
-
self.poly(:attack, opt:
|
72
|
+
self.poly(:attack, opt:) + self.poly(:str, opt:).quo(2) + self.poly(:dex, opt:).quo(2)
|
66
73
|
when :dex_as
|
67
|
-
self.poly(:dex, opt:
|
74
|
+
self.poly(:dex, opt:) + self.poly(:attack, opt:).quo(2) + self.poly(:str, opt:).quo(2)
|
68
75
|
when :mag_das
|
69
|
-
self.poly(:magic, opt:
|
76
|
+
self.poly(:magic, opt:) + self.poly(:dex_as, opt:).quo(2)
|
77
|
+
when :magic2
|
78
|
+
self.poly(:magic, opt:).scalar(2)
|
70
79
|
when :magmag
|
71
|
-
self.poly(:magdef, opt:
|
80
|
+
self.poly(:magdef, opt:) + self.poly(:magic, opt:).quo(2)
|
72
81
|
when :pmdef
|
73
|
-
pd = self.poly(:phydef, opt:
|
74
|
-
md = self.poly(:magmag, opt:
|
82
|
+
pd = self.poly(:phydef, opt:)
|
83
|
+
md = self.poly(:magmag, opt:)
|
75
84
|
pd <= md ? pd : md
|
76
85
|
when :cost
|
77
|
-
if Mgmg::SystemEquip.
|
86
|
+
if Mgmg::SystemEquip.has_key?(self)
|
78
87
|
return Mgmg::TPolynomial.new(Mgmg::Mat.new(1, 1, 0.quo(1)), 28, 0, 12, 12)
|
79
88
|
end
|
80
|
-
built = self.build(-1, opt:
|
89
|
+
built = self.build(-1, opt:)
|
81
90
|
const = (built.star**2) * ( /\+/.match(self) ? 5 : ( built.kind < 8 ? 2 : 1 ) )
|
82
|
-
ret = poly(:attack, opt:
|
83
|
-
ret += poly(:hp, opt:
|
84
|
-
ret += poly(:str, opt:
|
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:)
|
85
94
|
ret.mat.body[0][0] += const
|
86
95
|
ret
|
87
96
|
else
|
@@ -89,15 +98,15 @@ class String
|
|
89
98
|
end
|
90
99
|
end
|
91
100
|
def eff(para, smith, comp=smith, opt: Mgmg::Option.new)
|
92
|
-
a = build(smith, comp, opt:
|
93
|
-
b = build(smith+1, comp, opt:
|
94
|
-
c = build(smith, comp+2, opt:
|
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)
|
95
104
|
sden = smith==0 ? 1 : 2*smith-1
|
96
105
|
cden = comp==0 ? 4 : 8*comp
|
97
106
|
[(b-a).quo(sden), (c-a).quo(cden)]
|
98
107
|
end
|
99
108
|
def peff(para, smith, comp=smith, opt: Mgmg::Option.new)
|
100
|
-
poly(para, opt:
|
109
|
+
poly(para, opt:).eff(smith, comp)
|
101
110
|
end
|
102
111
|
def show(smith=-1, comp=smith, para: :power, opt: Mgmg::Option.new)
|
103
112
|
rein = case opt.reinforcement
|
@@ -106,38 +115,42 @@ class String
|
|
106
115
|
else
|
107
116
|
[Mgmg::Reinforcement.compile(opt.reinforcement)]
|
108
117
|
end
|
109
|
-
built = build(smith, comp, opt:
|
118
|
+
built = build(smith, comp, opt:)
|
110
119
|
pstr = '%.3f' % built.para_call(para)
|
111
120
|
pstr.sub!(/\.?0+\Z/, '')
|
112
|
-
puts "
|
121
|
+
puts "With levels (#{smith}, #{comp}: #{Mgmg.exp(smith, comp).comma3}), building"
|
113
122
|
puts " #{self}"
|
114
|
-
rein = rein.empty? ? '' : "
|
115
|
-
puts "
|
123
|
+
rein = rein.empty? ? '' : "reinforced by {#{rein.join(',')}} "
|
124
|
+
puts "#{rein}yields (#{pstr}, #{built.total_cost})"
|
116
125
|
puts " #{built}"
|
117
126
|
end
|
118
127
|
def phydef_optimize(smith=nil, comp=smith, opt: Mgmg::Option.new)
|
119
|
-
Mgmg::Optimize.phydef_optimize(self, smith, comp, opt:
|
128
|
+
Mgmg::Optimize.phydef_optimize(self, smith, comp, opt:)
|
120
129
|
end
|
121
130
|
def buster_optimize(smith=nil, comp=smith, opt: Mgmg::Option.new)
|
122
|
-
Mgmg::Optimize.buster_optimize(self, smith, comp, opt:
|
131
|
+
Mgmg::Optimize.buster_optimize(self, smith, comp, opt:)
|
123
132
|
end
|
124
133
|
end
|
125
134
|
module Enumerable
|
135
|
+
using Mgmg::Refiner
|
136
|
+
def to_recipe(para=:power, **kw)
|
137
|
+
Mgmg::Recipe.new(self, para, **kw)
|
138
|
+
end
|
126
139
|
def build(smith=-1, armor=smith, comp=armor.tap{armor=smith}, opt: Mgmg::Option.new)
|
127
140
|
opt = opt.dup
|
128
141
|
rein = opt.reinforcement
|
129
142
|
opt.reinforcement = []
|
130
143
|
self.sum(Mgmg::Equip::Zero) do |str|
|
131
|
-
if Mgmg::EquipPosition[str.build(opt:
|
132
|
-
str.build(smith, comp, opt:
|
144
|
+
if Mgmg::EquipPosition[str.build(opt:).kind] == 0
|
145
|
+
str.build(smith, comp, opt:)
|
133
146
|
else
|
134
|
-
str.build(armor, comp, opt:
|
147
|
+
str.build(armor, comp, opt:)
|
135
148
|
end
|
136
149
|
end.reinforce(*rein)
|
137
150
|
end
|
138
151
|
def ir(opt: Mgmg::Option.new)
|
139
152
|
self.sum(Mgmg::IR::Zero) do |str|
|
140
|
-
str.ir(opt:
|
153
|
+
str.ir(opt:)
|
141
154
|
end.add_reinforcement(opt.reinforcement)
|
142
155
|
end
|
143
156
|
def show(smith=-1, armor=smith, comp=armor.tap{armor=smith}, para: :power, opt: Mgmg::Option.new)
|
@@ -147,66 +160,66 @@ module Enumerable
|
|
147
160
|
else
|
148
161
|
[Mgmg::Reinforcement.compile(opt.reinforcement)]
|
149
162
|
end
|
150
|
-
built = self.build(smith, armor, comp, opt:
|
163
|
+
built = self.build(smith, armor, comp, opt:)
|
151
164
|
pstr = '%.3f' % built.para_call(para)
|
152
165
|
pstr.sub!(/\.?0+\Z/, '')
|
153
|
-
puts "
|
166
|
+
puts "With levels (#{smith}, #{armor}, #{comp}: #{Mgmg.exp(smith, armor, comp).comma3}), building"
|
154
167
|
puts " #{self.join(', ')}"
|
155
|
-
rein = rein.empty? ? '' : "
|
156
|
-
puts "
|
168
|
+
rein = rein.empty? ? '' : "reinforced by {#{rein.join(',')}} "
|
169
|
+
puts "#{rein}yields (#{pstr}, #{built.total_cost})"
|
157
170
|
puts " #{built}"
|
158
171
|
end
|
159
172
|
def min_weight(opt: Mgmg::Option.new)
|
160
|
-
build(*build(opt:
|
173
|
+
build(*build(opt:).min_levels_max, -1, opt:).weight
|
161
174
|
end
|
162
175
|
def max_weight(include_outsourcing=false, opt: Mgmg::Option.new)
|
163
176
|
if include_outsourcing
|
164
|
-
build(-1, opt:
|
177
|
+
build(-1, opt:).weight
|
165
178
|
else
|
166
|
-
build(*min_smith(opt:
|
179
|
+
build(*min_smith(opt:), -1, opt:).weight
|
167
180
|
end
|
168
181
|
end
|
169
182
|
def min_weights(opt: Mgmg::Option.new)
|
170
183
|
weapons, armors = [], []
|
171
184
|
each do |str|
|
172
|
-
if Mgmg::EquipPosition[str.build(opt:
|
185
|
+
if Mgmg::EquipPosition[str.build(opt:).kind] == 0
|
173
186
|
weapons << str
|
174
187
|
else
|
175
188
|
armors << str
|
176
189
|
end
|
177
190
|
end
|
178
|
-
[weapons.min_weight(opt:
|
191
|
+
[weapons.min_weight(opt:), armors.min_weight(opt:)]
|
179
192
|
end
|
180
193
|
def max_weights(include_outsourcing=false, opt: Mgmg::Option.new)
|
181
194
|
weapons, armors = [], []
|
182
195
|
each do |str|
|
183
|
-
if Mgmg::EquipPosition[str.build(opt:
|
196
|
+
if Mgmg::EquipPosition[str.build(opt:).kind] == 0
|
184
197
|
weapons << str
|
185
198
|
else
|
186
199
|
armors << str
|
187
200
|
end
|
188
201
|
end
|
189
|
-
[weapons.max_weight(include_outsourcing, opt:
|
202
|
+
[weapons.max_weight(include_outsourcing, opt:), armors.max_weight(include_outsourcing, opt:)]
|
190
203
|
end
|
191
204
|
def min_level(ws=0, wa=ws, include_outsourcing=false, opt: Mgmg::Option.new)
|
192
205
|
weapons, armors = [], []
|
193
206
|
each do |str|
|
194
|
-
if Mgmg::EquipPosition[str.build(opt:
|
207
|
+
if Mgmg::EquipPosition[str.build(opt:).kind] == 0
|
195
208
|
weapons << str
|
196
209
|
else
|
197
210
|
armors << str
|
198
211
|
end
|
199
212
|
end
|
200
|
-
ms, ma = min_smith(opt:
|
201
|
-
rs = min_level_sub(ws, ms, 0, weapons, include_outsourcing, opt:
|
202
|
-
ra = min_level_sub(wa, ma, 1, armors, include_outsourcing, opt:
|
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:)
|
203
216
|
[rs, ra]
|
204
217
|
end
|
205
218
|
private def min_level_sub(w, ms, i, recipe, include_outsourcing, opt: Mgmg::Option.new)
|
206
|
-
built = recipe.build(opt:
|
207
|
-
w = recipe.build(built.min_levels_max[i], opt:
|
219
|
+
built = recipe.build(opt:)
|
220
|
+
w = recipe.build(built.min_levels_max[i], opt:).weight - w if w <= 0
|
208
221
|
return -1 if include_outsourcing && built.weight <= w
|
209
|
-
return ms if build(ms, opt:
|
222
|
+
return ms if build(ms, opt:).weight <= w
|
210
223
|
ary = [ms]
|
211
224
|
4.downto(1) do |wi|
|
212
225
|
built.min_levels(wi).values.each do |v|
|
@@ -214,17 +227,17 @@ module Enumerable
|
|
214
227
|
end
|
215
228
|
end
|
216
229
|
ary.sort.each do |l|
|
217
|
-
return l if recipe.build(l, opt:
|
230
|
+
return l if recipe.build(l, opt:).weight <= w
|
218
231
|
end
|
219
|
-
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:
|
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:)}'."
|
220
233
|
end
|
221
234
|
def min_levels(w=1, opt: Mgmg::Option.new)
|
222
|
-
build(opt:
|
235
|
+
build(opt:).min_levels(w)
|
223
236
|
end
|
224
237
|
def min_levels_max(w=1, opt: Mgmg::Option.new)
|
225
238
|
ret = [-1, -1]
|
226
|
-
min_levels(w, opt:
|
227
|
-
if Mgmg::EquipPosition[str.build(opt:
|
239
|
+
min_levels(w, opt:).each do |str, level|
|
240
|
+
if Mgmg::EquipPosition[str.build(opt:).kind] == 0
|
228
241
|
ret[0] = [ret[0], level].max
|
229
242
|
else
|
230
243
|
ret[1] = [ret[1], level].max
|
@@ -235,8 +248,8 @@ module Enumerable
|
|
235
248
|
def min_smith(opt: Mgmg::Option.new)
|
236
249
|
ret = [-1, -1]
|
237
250
|
self.each do |str|
|
238
|
-
s = Mgmg::Equip.min_smith(str, opt:
|
239
|
-
if Mgmg::EquipPosition[str.build(opt:
|
251
|
+
s = Mgmg::Equip.min_smith(str, opt:)
|
252
|
+
if Mgmg::EquipPosition[str.build(opt:).kind] == 0
|
240
253
|
ret[0] = [ret[0], s].max
|
241
254
|
else
|
242
255
|
ret[1] = [ret[1], s].max
|
@@ -246,7 +259,7 @@ module Enumerable
|
|
246
259
|
end
|
247
260
|
def min_comp(opt: Mgmg::Option.new)
|
248
261
|
self.map do |str|
|
249
|
-
Mgmg::Equip.min_comp(str, opt:
|
262
|
+
Mgmg::Equip.min_comp(str, opt:)
|
250
263
|
end.append(-1).max
|
251
264
|
end
|
252
265
|
end
|
data/reference.md
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
# リファレンス
|
2
2
|
本ライブラリで定義される主要なメソッドを以下に解説します.
|
3
3
|
|
4
|
+
## `String#to_recipe(para=:power, allow_over20: false, **kw)`,`Enumerable#to_recipe(para=:power, **kw)`
|
5
|
+
レシピ文字列である`self`と,注目パラメータ`para`,オプション`Mgmg.option(**kw)`をセットにした[後述](#mgmgrecipe)の`Mgmg::Recipe`オブジェクトを生成して返します.デフォルト値としてセットされたパラメータを使用する以外は,レシピ文字列と同様に扱えます.
|
6
|
+
|
7
|
+
`allow_over20`が偽の場合,レシピの☆を確認し,20を超える場合は,例外`Mgmg::Over20Error`を発生します.このチェックを抑制したい場合は,真にしてください.
|
8
|
+
|
4
9
|
## `String#build(smith=-1, comp=smith, opt: Mgmg.option())`
|
5
10
|
レシピ文字列である`self`を解釈し,鍛冶・防具製作Lvを`smith`,道具製作Lvを`comp`として鍛冶・防具製作及び武器・防具合成を行った結果を[後述](#mgmgequip)の`Mgmg::Equip`クラスのインスタンスとして生成し,返します.例えば,
|
6
11
|
```ruby
|
@@ -145,7 +150,8 @@
|
|
145
150
|
返り値は`[逆転しない最大目標値, 逆転時の最小para値]`です.前者は逆転目標値の下限,後者は,目標値が前者よりも少しだけ大きいときの`para`値です.
|
146
151
|
ここで,最小経験値が小さい方,または最小経験値が同じなら,そのときの`para`値が大きい方をよりよいものと解釈します.
|
147
152
|
`term`は`start`より大きい値とします.目標値`term`における優劣が,目標値`start`における優劣と同じ場合,`Mgmg::SearchCutException`を発生します.
|
148
|
-
|
153
|
+
|
154
|
+
`a`と`b`は`String`でもその`Enumerable`でも構いません.`Mgmg::Recipe`が与えられた場合,`opt_a`,`opt_b`は無視され,代わりに`a.option`,`b.option`が使用されます.`a.para`,`b.para`は無視され,引数で与えた`para`を使用します.
|
149
155
|
|
150
156
|
`opt_a`,`opt_b`には,それぞれ`a`と`b`に関するオプションパラメータを指定します.`smith_min`,`armor_min`,`min_smith`,`left_associative`,`reinforcement`,`irep`を使用します.
|
151
157
|
|
@@ -451,3 +457,55 @@ alias として`*`があるほか`scalar(1.quo(value))`として`quo`,`/`,`s
|
|
451
457
|
|irep|`recipe.ir()`|`Mgmg::IR`の使い回しによる高速化|`String#search`など,内部的に使用|
|
452
458
|
|cut_exp|`Float::INFINITY`|探索時の総経験値の打ち切り値|`String#search`など,内部的に使用|
|
453
459
|
|
460
|
+
## `Mgmg::Recipe`
|
461
|
+
レシピ文字列,注目パラメータ,オプションをセットにして扱うためのクラスです.
|
462
|
+
それぞれ`Mgmg::Recipe#recipe`,`Mgmg::Recipe#para`,`Mgmg::Recipe#option`でアクセスできます.
|
463
|
+
|
464
|
+
## `Mgmg::Recipe#option(**kw)`
|
465
|
+
オプションパラメータを`kw`で上書きして,新しい`Mgmg::Option`インスタンスを返します.
|
466
|
+
渡したバラメータだけ上書きします.パラメータを渡さない場合,単にセットになってるオプションオブジェクトを返します.
|
467
|
+
`target_weight`を上書きした場合,`smith_min`,`armor_min`は自動的に再計算されます.
|
468
|
+
|
469
|
+
与えるパラメータ以外をデフォルト値に戻したい場合,`recipe.option=Mgmg.option(**kw)`のように`Mgmg::Recipe#option=(new_option)`を使用します.
|
470
|
+
|
471
|
+
## `Mgmg::Recipe#replace(new_recipe, para: self.para, **kw)`
|
472
|
+
レシピ文字列を`new_recipe`で置き換え,`kw`でオプションを設定します.`para`を与えた場合,注目パラメータも引数で置き換えます.
|
473
|
+
レシピ文字列とオプションの対応を保つため,`Mgmg::Recipe#recipe=`メソッドは定義していません.代わりにこれを使います.
|
474
|
+
|
475
|
+
## `Mgmg::Recipe#build(smith=-1, armor=smith, comp=armor.tap{armor=smith}, **kw)`
|
476
|
+
`self.recipe.build`を呼び出して返します.`kw`が与えられた場合,オプションのうち,与えられたパラメータのみ一時的に上書きします.
|
477
|
+
|
478
|
+
`smith`に`nil`を与えた場合,製作Lvを`self.option.smith_min`,`self.option.armor_min`,`self.option.comp_min`とします.
|
479
|
+
|
480
|
+
## `Mgmg::Recipe#search(target, para: self.para, **kw)`
|
481
|
+
`self.recipe.search`を呼び出して返します.注目パラメータはセットされたものを自動的に渡しますが,別のパラメータを使いたい場合,キーワード引数で指定します.その他のキーワード引数を与えた場合,オプションのうち,与えられたパラメータのみ一時的に上書きします.
|
482
|
+
|
483
|
+
## `Mgmg::Recipe#min_level(w=self.option.target_weight, include_outsourcing=false)`
|
484
|
+
`self.recipe.min_level(w, include_outsourcing)`を返します.`w`のデフォルトパラメータが`target_weight`になっている以外は`String#min_level`または`Enumerable#min_level`と同じです.
|
485
|
+
|
486
|
+
## `Mgmg::Recipe#min_weight, max_weight, min_levels, min_levels_max, min_smith, min_comp`
|
487
|
+
`self.recipe`の同名メソッドをそのまま呼び出して返します.
|
488
|
+
|
489
|
+
## `Mgmg::Recipe#ir(**kw)`
|
490
|
+
`self.recipe`に対応した`Mgmg::IR`インスタンスを返します.
|
491
|
+
|
492
|
+
`kw`を与えた場合一時的に置き換えたオプションに対応した`Mgmg::IR`インスタンスを返します.`left_associative`が与えられれば再構築し,`reinforcement`が与えられれば,スキル・料理の効果を与えた引数に差し替えます.その他のキーワードを与えても特に影響はありません.
|
493
|
+
|
494
|
+
## `Mgmg::Recipe#para_call(smith=-1, armor=smith, comp=armor.tap{armor=smith}, para: self.para, **kw)`
|
495
|
+
`self.ir.para_call`を呼び出して返します.キーワード引数が与えられた場合,与えられた分だけ一時的に上書きします.
|
496
|
+
|
497
|
+
`smith`に`nil`を与えた場合,製作Lvを`self.option.smith_min`,`self.option.armor_min`,`self.option.comp_min`とします.製作Lvに負の値が与えられた場合,`0`に修正して計算して返します.
|
498
|
+
|
499
|
+
パラメータとして,以下が指定でき,これらは直接メソッドとしても定義されています(`Mgmg::Recipe#attack`など).製作Lvの与え方は`para_call`と同様です.
|
500
|
+
|
501
|
+
```ruby
|
502
|
+
:attack, :phydef, :magdef, :hp, :mp, :str, :dex, :speed, :magic
|
503
|
+
:atkstr, :atk_sd, :dex_as, :mag_das, :magic2, :magmag, :pmdef
|
504
|
+
:power, :fpower, :smith_cost, :comp_cost, :cost
|
505
|
+
```
|
506
|
+
|
507
|
+
## `Mgmg::Recipe#poly(para=self.para, **kw)`
|
508
|
+
`self.recipe.poly(para, opt: self.option)`を返します.`kw`が与えられた場合,オプションのうち,与えられたパラメータのみ一時的に上書きします.
|
509
|
+
|
510
|
+
## `Mgmg::Recipe#phydef_optimize(smith=nil, comp=smith, **kw)`,`Mgmg::Recipe#buster_optimize(smith=nil, comp=smith, **kw)`
|
511
|
+
`self.recipe.phydef_optimize(smith, comp, opt: self.option)`,`self.recipe.buster_optimize(smith, comp, opt: self.option)`を返します.`kw`が与えられた場合,オプションのうち,与えられたパラメータのみ一時的に上書きします.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mgmg
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.5.
|
4
|
+
version: 1.5.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- KAZOON
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-06-
|
11
|
+
date: 2022-06-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -75,6 +75,7 @@ files:
|
|
75
75
|
- lib/mgmg/optimize.rb
|
76
76
|
- lib/mgmg/option.rb
|
77
77
|
- lib/mgmg/poly.rb
|
78
|
+
- lib/mgmg/recipe.rb
|
78
79
|
- lib/mgmg/reinforce.rb
|
79
80
|
- lib/mgmg/search.rb
|
80
81
|
- lib/mgmg/system_equip.rb
|