cw_card_utils 0.1.11 → 0.1.12

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.
@@ -2,12 +2,18 @@
2
2
 
3
3
  module CwCardUtils
4
4
  module DecklistParser
5
- # A Deck is a collection of cards.
5
+ # Public: A deck is a collection of cards and utilities around it.
6
+ # 日本語: デッキはカードの集合であり、各種ユーティリティを提供します。
6
7
  class Deck
7
8
  IGNORED_TRIBAL_TYPES = %w[
8
9
  human wizard soldier scout shaman warrior cleric rogue advisor knight druid
9
10
  ].freeze
10
11
 
12
+ # Public: Initialize an empty deck with a data source.
13
+ # 日本語: データソースを指定して空のデッキを初期化します。
14
+ #
15
+ # @param cmc_data_source [CwCardUtils::CardDataSource]
16
+ # @return [void]
11
17
  def initialize(cmc_data_source)
12
18
  @main = []
13
19
  @sideboard = []
@@ -17,6 +23,10 @@ module CwCardUtils
17
23
  @cmc_data_source = cmc_data_source
18
24
  end
19
25
 
26
+ # Public: Serialize as a Hash suitable for JSON.
27
+ # 日本語: JSON化に適したHashへシリアライズします。
28
+ #
29
+ # @return [Hash]
20
30
  def to_h
21
31
  {
22
32
  mainboard: main.map(&:to_h),
@@ -25,26 +35,61 @@ module CwCardUtils
25
35
  }
26
36
  end
27
37
 
38
+ # Public: Unique color identity across mainboard.
39
+ # 日本語: メインボードの色アイデンティティ集合。
40
+ #
41
+ # @return [Array<String>]
28
42
  def color_identity
29
43
  @color_identity ||= main.map(&:color_identity).flatten.uniq
30
44
  end
31
45
 
46
+ # Public: Human readable color label.
47
+ # 日本語: 人間が読みやすい色表記。
48
+ #
49
+ # @return [String]
32
50
  def color_identity_string
33
51
  @color_identity_string ||= CwCardUtils::DecklistParser::ColorIdentityResolver.resolve(color_identity)
34
52
  end
35
53
 
54
+ # Public: JSON representation of the deck.
55
+ # 日本語: デッキのJSON表現。
56
+ #
57
+ # @return [String]
36
58
  def to_json(*_args)
37
59
  to_h.to_json
38
60
  end
39
61
 
62
+ # Public: Mainboard cards with tags applied.
63
+ # 日本語: タグ適用済みのメインボードカード一覧。
64
+ #
65
+ # How it works (EN): Lazily computes tags for each card via
66
+ # `CardTagger` the first time access occurs, then returns the
67
+ # mutated card objects for downstream consumers.
68
+ #
69
+ # 仕組み (JA): 初回アクセス時に `CardTagger` で各カードのタグを
70
+ # 遅延計算し、以降はタグ付け済みカードを返します。
71
+ #
72
+ # @return [Array<CwCardUtils::DecklistParser::Card>]
40
73
  def main
41
74
  tag_cards(@main)
42
75
  end
43
76
 
77
+ # Public: Sideboard cards with tags applied.
78
+ # 日本語: タグ適用済みのサイドボードカード一覧。
79
+ #
80
+ # How it works (EN): Same lazy-tagging behavior as `main`, but for
81
+ # sideboard entries.
82
+ #
83
+ # 仕組み (JA): `main` と同様に遅延タグ付けを行いますが、対象は
84
+ # サイドボードです。
85
+ #
86
+ # @return [Array<CwCardUtils::DecklistParser::Card>]
44
87
  def sideboard
45
88
  tag_cards(@sideboard)
46
89
  end
47
90
 
91
+ # Internal: Apply tags to cards lazily.
92
+ # 日本語: 遅延的にカードへタグを適用します。
48
93
  def tag_cards(cards)
49
94
  cards.map do |c|
50
95
  c.tags = CwCardUtils::DecklistParser::CardTagger.new(c, self).tags
@@ -52,44 +97,117 @@ module CwCardUtils
52
97
  end
53
98
  end
54
99
 
100
+ # Public: Detected archetype label.
101
+ # 日本語: 検出されたアーキタイプのラベル。
102
+ #
103
+ # Intent (EN): Summarize the deck’s strategic identity using
104
+ # aggregated tag ratios/counts and average CMC.
105
+ #
106
+ # 意図 (JA): タグの比率/件数と平均 CMC を用いて、デッキの戦略的
107
+ # アイデンティティを要約します。
108
+ #
109
+ # @return [String]
55
110
  def archetype
56
111
  @archetype ||= CwCardUtils::DecklistParser::ArchetypeDetector.new(self).detect
57
112
  end
58
113
 
114
+ # Public: Human-friendly deck summary string.
115
+ # 日本語: 人が読みやすいデッキ概要文字列。
116
+ #
117
+ # @return [String]
59
118
  def inspect
60
119
  "<Deck: main: #{mainboard_size} sideboard: #{sideboard_size} lands: #{lands_count} x_to_cast: #{x_to_cast_count} cards: #{cards_count}>"
61
120
  end
62
121
 
122
+ # Public: Collapsed curve counts.
123
+ # 日本語: まとめられたカーブのカウント。
124
+ #
125
+ # Intent (EN): Provide a coarse view of the deck’s curve using
126
+ # canonical buckets for quick comparison.
127
+ #
128
+ # 意図 (JA): 代表的なバケットでカーブを粗く表し、比較を容易に
129
+ # します。
130
+ #
131
+ # @return [Hash{String=>Integer}]
63
132
  def collapsed_curve
64
133
  @collapsed_curve ||= CurveCalculator.new(self).collapsed_curve
65
134
  end
66
135
 
136
+ # Public: Raw curve counts.
137
+ # 日本語: 素のカーブカウント。
138
+ #
139
+ # Intent (EN): Exact histogram of non-land CMC ceilings used by
140
+ # calculators and visualizations.
141
+ #
142
+ # 意図 (JA): 計算や可視化で用いる、土地以外の CMC 切り上げの
143
+ # ヒストグラムです。
144
+ #
145
+ # @return [Hash{Integer=>Integer}]
67
146
  def curve
68
147
  @curve ||= CurveCalculator.new(self).curve
69
148
  end
70
149
 
150
+ # Public: Normalized curve fractions.
151
+ # 日本語: 正規化されたカーブ比率。
152
+ #
153
+ # Intent (EN): Convert raw counts into proportions of non-land
154
+ # cards to compare decks of different sizes.
155
+ #
156
+ # 意図 (JA): デッキサイズが異なる場合でも比較できるよう、土地以外
157
+ # の枚数で割った比率に変換します。
158
+ #
159
+ # @return [Hash{Integer=>Float}]
71
160
  def normalized_curve
72
161
  @normalized_curve ||= CurveCalculator.new(self).normalized_curve
73
162
  end
74
163
 
164
+ # Public: Collapsed normalized curve.
165
+ # 日本語: まとめられかつ正規化されたカーブ。
166
+ #
167
+ # Intent (EN): Normalize the collapsed buckets for concise and
168
+ # comparable summaries.
169
+ #
170
+ # 意図 (JA): まとめたバケットを正規化し、簡潔かつ比較しやすい
171
+ # 要約を提供します。
172
+ #
173
+ # @return [Hash{String=>Float}]
75
174
  def collapsed_normalized_curve
76
175
  @collapsed_normalized_curve ||= CurveCalculator.new(self).collapsed_normalized_curve
77
176
  end
78
177
 
178
+ # Public: True when both main and sideboard are empty.
179
+ # 日本語: メイン・サイドの両方が空であれば true。
180
+ #
181
+ # @return [Boolean]
79
182
  def empty?
80
183
  @main.empty? && @sideboard.empty?
81
184
  end
82
185
 
186
+ # Public: True when deck has any cards.
187
+ # 日本語: 1枚でもカードがあれば true。
188
+ #
189
+ # @return [Boolean]
83
190
  def any?
84
191
  !empty?
85
192
  end
86
193
 
194
+ # Public: Enumerate mainboard cards.
195
+ # 日本語: メインボードのカードを列挙します。
196
+ #
197
+ # @yield [CwCardUtils::DecklistParser::Card] カードをブロックに渡します
198
+ # @return [void]
87
199
  def each(&block)
88
200
  @main.each do |c|
89
201
  block.call(c)
90
202
  end
91
203
  end
92
204
 
205
+ # Public: Add a card hash to mainboard or sideboard.
206
+ # 日本語: カードをメイン/サイドに追加します。
207
+ #
208
+ # @param c [Hash] { name:, count: }
209
+ # @param target [Symbol] :mainboard or :sideboard
210
+ # @return [void]
93
211
  def add(c, target = :mainboard)
94
212
  reset_counters
95
213
  card = Card.new(c[:name], c[:count], @cmc_data_source)
@@ -108,6 +226,8 @@ module CwCardUtils
108
226
  end
109
227
  end
110
228
 
229
+ # Internal: Invalidate derived counters.
230
+ # 日本語: 派生カウンタを無効化します。
111
231
  def reset_counters
112
232
  @mainboard_size = nil
113
233
  @sideboard_size = nil
@@ -116,10 +236,26 @@ module CwCardUtils
116
236
  @cards_count = nil
117
237
  end
118
238
 
239
+ # Public: Detected format (:commander, :standard, :modern)。
240
+ # 日本語: 判定されたフォーマット (:commander, :standard, :modern)。
241
+ #
242
+ # @return [Symbol]
119
243
  def format
120
244
  @format ||= detect_format_for_deck
121
245
  end
122
246
 
247
+ # Public: Detect the format from deck size and singleton rules.
248
+ # 日本語: デッキサイズとシングルトン規則からフォーマットを判定します。
249
+ #
250
+ # How it works (EN): Uses size thresholds (>=100 => Commander),
251
+ # then checks for singleton behavior ignoring basic lands to
252
+ # disambiguate Commander-like 60+ lists from Standard.
253
+ #
254
+ # 仕組み (JA): サイズ閾値 (>=100 は Commander) を用い、基本土地を
255
+ # 無視したシングルトン性を確認して、Standard と似た 60+ の
256
+ # Commander らしさを判別します。
257
+ #
258
+ # @return [Symbol]
123
259
  def detect_format_for_deck
124
260
  if mainboard_size >= 100
125
261
  :commander
@@ -135,6 +271,8 @@ module CwCardUtils
135
271
  end
136
272
  end
137
273
 
274
+ # Internal: Whether the deck is singleton ignoring basic lands.
275
+ # 日本語: 基本土地を無視してシングルトンかどうか。
138
276
  def is_singleton_deck?
139
277
  # Count non-basic land cards and check for duplicates
140
278
  non_basic_lands = @main.reject { |card| is_basic_land?(card) }
@@ -149,44 +287,82 @@ module CwCardUtils
149
287
  card_counts.values.all? { |count| count == 1 }
150
288
  end
151
289
 
290
+ # Internal: Whether a card is a basic land.
291
+ # 日本語: 基本土地であるかどうか。
152
292
  def is_basic_land?(card)
153
293
  basic_land_names = %w[Plains Island Swamp Mountain Forest Wastes]
154
294
  basic_land_names.include?(card.name)
155
295
  end
156
296
 
297
+ # Public: Total mainboard card count.
298
+ # 日本語: メインボードの総枚数。
299
+ #
300
+ # @return [Integer]
157
301
  def mainboard_size
158
302
  @mainboard_size ||= main.sum { |card| card.count }
159
303
  end
160
304
 
305
+ # Public: Total sideboard card count.
306
+ # 日本語: サイドボードの総枚数。
307
+ #
308
+ # @return [Integer]
161
309
  def sideboard_size
162
310
  @sideboard_size ||= sideboard.sum { |card| card.count }
163
311
  end
164
312
 
313
+ # Public: Land count.
314
+ # 日本語: 土地の枚数。
315
+ #
316
+ # @return [Integer]
165
317
  def lands_count
166
318
  @lands.sum { |card| card.count }
167
319
  end
168
320
 
321
+ # Public: X-to-cast count.
322
+ # 日本語: Xコストのカード枚数。
323
+ #
324
+ # @return [Integer]
169
325
  def x_to_cast_count
170
326
  @x_to_cast.sum { |card| card.count }
171
327
  end
172
328
 
329
+ # Public: Total main+sideboard count.
330
+ # 日本語: メイン+サイドの合計枚数。
331
+ #
332
+ # @return [Integer]
173
333
  def cards_count
174
334
  mainboard_size + sideboard_size
175
335
  end
176
336
 
337
+ # Public: Total non-land count.
338
+ # 日本語: 土地以外の総枚数。
339
+ #
340
+ # @return [Integer]
177
341
  def count_without_lands
178
342
  cards_count - lands_count
179
343
  end
180
344
 
345
+ # Public: Deck size including sideboard.
346
+ # 日本語: サイドボードを含むデッキサイズ。
347
+ #
348
+ # @return [Integer]
181
349
  def size
182
350
  cards_count
183
351
  end
184
352
 
353
+ # Public: Dominant tribe symbol or nil.
354
+ # 日本語: 優勢な部族(トライブ)のシンボル、もしくは nil。
355
+ #
356
+ # @return [Symbol, nil]
185
357
  def tribe
186
358
  return @tribe if @tribe
187
359
  @tribe = detect_tribe_for_deck
188
360
  end
189
361
 
362
+ # Public: Detect dominant creature subtype if present.
363
+ # 日本語: 優勢なクリーチャー・サブタイプがあれば検出します。
364
+ #
365
+ # @return [Symbol, nil]
190
366
  def detect_tribe_for_deck
191
367
  subtype_counts = Hash.new(0)
192
368
  total_creatures = 0
@@ -3,19 +3,45 @@
3
3
  module CwCardUtils
4
4
  module DecklistParser
5
5
  # Parses a decklist and returns a Deck object.
6
+ # Public: Parses a decklist string/IO into a Deck.
7
+ # 日本語: デッキリスト文字列/IOを解析して Deck を生成します。
6
8
  class Parser
7
9
  attr_reader :deck
8
10
 
11
+ # Public: Create a new parser.
12
+ # 日本語: 新しいパーサを作成します。
13
+ #
14
+ # @param decklist [String, IO]
15
+ # @param cmc_data_source [CwCardUtils::CardDataSource]
16
+ # @return [void]
9
17
  def initialize(decklist, cmc_data_source = nil)
10
18
  @decklist = decklist.is_a?(IO) ? decklist.read : decklist
11
19
  @deck = Deck.new(cmc_data_source || CwCardUtils.card_data_source)
12
20
  end
13
21
 
22
+ # Public: Human-readable summary.
23
+ # 日本語: 人間が読みやすい概要。
24
+ #
25
+ # @return [String]
14
26
  def inspect
15
27
  "<DecklistParser::Parser: #{@decklist.length}>"
16
28
  end
17
29
 
18
30
  # Parses the decklist and returns a Deck object.
31
+ # Public: Parse and return a Deck. Idempotent.
32
+ # 日本語: 解析して Deck を返します。多重実行に耐性があります。
33
+ #
34
+ # How it works (EN): Streams the decklist line-by-line, skipping
35
+ # section headers and comments, toggling sideboard state as
36
+ # appropriate, and parsing counts/names using a permissive regex
37
+ # that tolerates set codes and collector numbers.
38
+ #
39
+ # 仕組み (JA): デッキリストを 1 行ずつ処理し、セクション見出しや
40
+ # コメントをスキップ、適宜サイドボード状態を切替えながら、
41
+ # 枚数とカード名を寛容な正規表現で抽出します (セットコードや
42
+ # コレクタ番号を許容)。
43
+ #
44
+ # @return [CwCardUtils::DecklistParser::Deck]
19
45
  def parse
20
46
  return @deck if @deck.any?
21
47
 
@@ -4,12 +4,23 @@ require "json"
4
4
  require "singleton"
5
5
 
6
6
  module CwCardUtils
7
- # Abstract base class for card data sources
7
+ # Public: Abstract base class for card data sources.
8
+ # 日本語: カードデータソースの抽象基底クラスです。
8
9
  class CardDataSource
10
+ # Public: Find a card record by name.
11
+ # 日本語: カード名でレコードを検索します。
12
+ #
13
+ # @param name [String]
14
+ # @return [Hash, #dig, nil]
9
15
  def find_card(name)
10
16
  raise NotImplementedError, "Subclasses must implement find_card"
11
17
  end
12
18
 
19
+ # Public: Fetch converted mana cost for card.
20
+ # 日本語: 指定カードの点数で見たマナ・コストを取得します。
21
+ #
22
+ # @param name [String]
23
+ # @return [Numeric, nil]
13
24
  def cmc_for_card(name)
14
25
  card = find_card(name)
15
26
  card&.dig("cmc") || card&.cmc
@@ -17,6 +28,11 @@ module CwCardUtils
17
28
  nil
18
29
  end
19
30
 
31
+ # Public: Fetch color identity for card.
32
+ # 日本語: 指定カードの色アイデンティティを取得します。
33
+ #
34
+ # @param name [String]
35
+ # @return [Array<String>]
20
36
  def color_identity_for_card(name)
21
37
  card = find_card(name)
22
38
  card&.dig("color_identity") || card&.color_identity || []
@@ -24,6 +40,11 @@ module CwCardUtils
24
40
  []
25
41
  end
26
42
 
43
+ # Public: Fetch keywords for card.
44
+ # 日本語: 指定カードのキーワードを取得します。
45
+ #
46
+ # @param name [String]
47
+ # @return [Array<String>]
27
48
  def keywords_for_card(name)
28
49
  card = find_card(name)
29
50
  card&.dig("keywords") || card&.keywords || []
@@ -31,6 +52,11 @@ module CwCardUtils
31
52
  []
32
53
  end
33
54
 
55
+ # Public: Fetch toughness for card.
56
+ # 日本語: 指定カードのタフネスを取得します。
57
+ #
58
+ # @param name [String]
59
+ # @return [String, Integer, nil]
34
60
  def toughness_for_card(name)
35
61
  card = find_card(name)
36
62
  card&.dig("toughness") || card&.toughness
@@ -38,6 +64,11 @@ module CwCardUtils
38
64
  nil
39
65
  end
40
66
 
67
+ # Public: Fetch power for card.
68
+ # 日本語: 指定カードのパワーを取得します。
69
+ #
70
+ # @param name [String]
71
+ # @return [String, Integer, nil]
41
72
  def power_for_card(name)
42
73
  card = find_card(name)
43
74
  card&.dig("power") || card&.power
@@ -45,6 +76,11 @@ module CwCardUtils
45
76
  nil
46
77
  end
47
78
 
79
+ # Public: Fetch oracle text for card.
80
+ # 日本語: 指定カードのオラクルテキストを取得します。
81
+ #
82
+ # @param name [String]
83
+ # @return [String, nil]
48
84
  def oracle_text_for_card(name)
49
85
  card = find_card(name)
50
86
  card&.dig("oracle_text") || card&.oracle_text
@@ -52,6 +88,11 @@ module CwCardUtils
52
88
  nil
53
89
  end
54
90
 
91
+ # Public: Fetch type line for card (nil for Lands).
92
+ # 日本語: 指定カードのタイプ行を取得します (土地は nil)。
93
+ #
94
+ # @param name [String]
95
+ # @return [String, nil]
55
96
  def type_for_card(name)
56
97
  card = find_card(name)
57
98
  type = card&.dig("type_line") || card&.type_line
@@ -62,14 +103,24 @@ module CwCardUtils
62
103
  end
63
104
  end
64
105
 
65
- # Example MongoDB data source implementation
66
- # Users can implement their own data sources by inheriting from CardDataSource
106
+ # Public: Example MongoDB data source implementation.
107
+ # 日本語: MongoDB を用いた例示的なデータソース実装です。
67
108
  class MongoCardDataSource < CardDataSource
109
+ # Public: Initialize with a Mongo-like collection.
110
+ # 日本語: Mongo 互換コレクションで初期化します。
111
+ #
112
+ # @param collection [#find_one]
113
+ # @return [void]
68
114
  def initialize(collection)
69
115
  super()
70
116
  @collection = collection
71
117
  end
72
118
 
119
+ # Public: Find a card by exact name.
120
+ # 日本語: カード名の完全一致で検索します。
121
+ #
122
+ # @param name [String]
123
+ # @return [Hash, nil]
73
124
  def find_card(name)
74
125
  @collection.find_one({ "name" => name })
75
126
  rescue StandardError
@@ -77,44 +128,58 @@ module CwCardUtils
77
128
  end
78
129
  end
79
130
 
80
- # Represents a card with Scryfall data
131
+ # Public: Represents a card with Scryfall data.
132
+ # 日本語: Scryfall データを持つカードの表現です。
81
133
  class ScryfallCard
134
+ # Public: Wrap a Scryfall-backed card object.
135
+ # 日本語: Scryfall データに基づくカードをラップします。
136
+ #
137
+ # @param name [String]
138
+ # @param data_source [CwCardUtils::CardDataSource, nil]
139
+ # @return [void]
82
140
  def initialize(name, data_source = nil)
83
141
  @name = name
84
142
  @data = (data_source || CwCardUtils.card_data_source).find_card(@name) || {}
85
143
  end
86
144
 
145
+ # @return [Numeric, nil]
87
146
  def cmc
88
147
  @data["cmc"]
89
148
  end
90
149
 
150
+ # @return [String, nil]
91
151
  def type
92
152
  @data["type_line"]
93
153
  end
94
154
 
155
+ # @return [Array<String>, nil]
95
156
  def keywords
96
157
  @data["keywords"]
97
158
  end
98
159
 
160
+ # @return [String, Integer, nil]
99
161
  def power
100
162
  @data["power"]
101
163
  end
102
164
 
165
+ # @return [String, Integer, nil]
103
166
  def toughness
104
167
  @data["toughness"]
105
168
  end
106
169
 
170
+ # @return [String, nil]
107
171
  def oracle_text
108
172
  @data["oracle_text"]
109
173
  end
110
174
 
175
+ # @return [Array<String>, nil]
111
176
  def color_identity
112
177
  @data["color_identity"]
113
178
  end
114
179
  end
115
180
 
116
- # Handles conversion mana cost (CMC) data for Magic: The Gathering cards
117
- # Uses Singleton pattern to avoid loading the large JSON file multiple times
181
+ # Public: Singleton accessor for Scryfall-based CMC/type data.
182
+ # 日本語: Scryfall ベースの CMC/タイプ情報へのシングルトンアクセスを提供します。
118
183
  class ScryfallCmcData < CardDataSource
119
184
  include Singleton
120
185
 
@@ -125,6 +190,11 @@ module CwCardUtils
125
190
  @found_cards = {}
126
191
  end
127
192
 
193
+ # Public: Find a card hash by name, using index or linear search.
194
+ # 日本語: インデックスまたは線形探索でカード名からデータを検索します。
195
+ #
196
+ # @param name [String]
197
+ # @return [Hash, nil]
128
198
  def find_card(name)
129
199
  load_data_if_needed
130
200
  @card_index[name] || @found_cards[name] ||= linear_search(name)
@@ -132,6 +202,18 @@ module CwCardUtils
132
202
  nil
133
203
  end
134
204
 
205
+ # Public: Full CMC dataset as parsed JSON array.
206
+ # 日本語: 解析済みの JSON 配列データ全体を返します。
207
+ #
208
+ # How it works (EN): Lazily loads the bundled JSON once, building an
209
+ # in-memory hash index by card name for fast lookups. Subsequent
210
+ # calls reuse the cached data.
211
+ #
212
+ # 仕組み (JA): バンドルされた JSON を遅延読み込みし、カード名をキー
213
+ # にしたインメモリのハッシュインデックスを構築します。以降の呼び出し
214
+ # ではキャッシュを再利用します。
215
+ #
216
+ # @return [Array<Hash>]
135
217
  def cmc_data
136
218
  load_data_if_needed
137
219
  @data
@@ -139,6 +221,8 @@ module CwCardUtils
139
221
 
140
222
  private
141
223
 
224
+ # Internal: Lazy-load JSON and build index once.
225
+ # 日本語: JSON を遅延読み込みし、インデックスを一度だけ構築します。
142
226
  def load_data_if_needed
143
227
  return if @data
144
228
 
@@ -146,6 +230,8 @@ module CwCardUtils
146
230
  build_card_index
147
231
  end
148
232
 
233
+ # Internal: Build hash from card name to card info.
234
+ # 日本語: カード名から情報へのハッシュインデックスを作成します。
149
235
  def build_card_index
150
236
  @card_index = {}
151
237
  @data.each do |card|
@@ -153,6 +239,8 @@ module CwCardUtils
153
239
  end
154
240
  end
155
241
 
242
+ # Internal: Linear scan fallback by exact name.
243
+ # 日本語: 完全一致名での線形探索フォールバック。
156
244
  def linear_search(name)
157
245
  @data.find { |card| card["name"] == name }
158
246
  end
@@ -1,14 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CwCardUtils
4
- # Calculates probability of drawing specific card combinations for synergy analysis
4
+ # Public: Calculates probability of drawing specific card combinations.
5
+ # 日本語: 特定カードの組み合わせを引く確率を計算します。
5
6
  class SynergyProbability
7
+ # Public: Initialize with a deck and its size.
8
+ # 日本語: デッキとそのサイズで初期化します。
9
+ #
10
+ # @param deck [CwCardUtils::DecklistParser::Deck]
11
+ # @param deck_size [Integer]
12
+ # @return [void]
6
13
  def initialize(deck, deck_size: 60)
7
14
  @deck = deck
8
15
  @deck_size = deck_size
9
16
  end
10
17
 
11
18
  # Probability of drawing at least one of the target cards
19
+ # Public: Probability of drawing at least one of targets in given draws.
20
+ # 日本語: 指定枚数のドローで、対象のいずれか1枚以上を引く確率。
21
+ #
22
+ # How it works (EN): Uses the complement rule with hypergeometric
23
+ # coefficients: 1 - C(N - T, d) / C(N, d), where N is deck size,
24
+ # T is total copies of targets, and d is draws (clamped).
25
+ #
26
+ # 仕組み (JA): 超幾何係数の補集合を用います。N をデッキ枚数、T を
27
+ # 対象の総枚数、d をドロー枚数 (クランプ済み) とすると、
28
+ # 1 - C(N - T, d) / C(N, d) で計算します。
29
+ #
30
+ # @param target_names [Array<String>]
31
+ # @param draws [Integer]
32
+ # @return [Float] 0.0..1.0
12
33
  def prob_single(target_names, draws)
13
34
  targets = Array(target_names).uniq
14
35
  draws_clamped = clamp_draws(draws)
@@ -19,6 +40,19 @@ module CwCardUtils
19
40
  end
20
41
 
21
42
  # Probability of drawing ALL cards in the targets list (synergy pair/trio)
43
+ # Public: Probability of drawing all targets in given draws.
44
+ # 日本語: 指定枚数のドローで対象の全カードを揃える確率。
45
+ #
46
+ # How it works (EN): For 1/2/3-card combos, uses exact inclusion–
47
+ # exclusion with hypergeometric terms. For 4+ cards, uses a
48
+ # conservative approximation by summing miss-probabilities.
49
+ #
50
+ # 仕組み (JA): 1/2/3 枚コンボは超幾何項の包除原理で厳密に算出。
51
+ # 4 枚以上では各カードの不在確率を合算する保守的近似を用います。
52
+ #
53
+ # @param target_names [Array<String>]
54
+ # @param draws [Integer]
55
+ # @return [Float] 0.0..1.0
22
56
  def prob_combo(target_names, draws)
23
57
  targets = Array(target_names).uniq
24
58
  case targets.size
@@ -1,5 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ ##
4
+ # Public: Version of the CwCardUtils gem.
5
+ # 日本語: この CwCardUtils gem のバージョン定義です。
3
6
  module CwCardUtils
4
- VERSION = "0.1.11"
7
+ # Public: The current version string.
8
+ # 日本語: 現在のバージョン文字列です。
9
+ #
10
+ # @return [String]
11
+ VERSION = "0.1.12"
5
12
  end