cw_card_utils 0.1.10 → 0.1.11
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/lib/cw_card_utils/synergy_probability.rb +60 -35
- data/lib/cw_card_utils/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 23145f32617794cf1af83523bf179d6d9260cc75251a24207ae6ea6ce0274599
|
4
|
+
data.tar.gz: 7cd65b1d7e93b8f9c2438df235d77c44e294d543ff1f22e8b0aa16efb5e51192
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 556bc7d3c4dc4d87b752d2923eba061b23356a6e2330f33691e34ee215d7cdd9e6148cd8c4a9082ddbcda179ffd68bef162d0bc6e11c25db3bc05b95589f83d9
|
7
|
+
data.tar.gz: e81abacff1c5dae3476a1ffb9e6a779105546f7a020edcc36fb463082842645a784e922d89660c2e779c4ceaa1d51dadf14b84ed4ae33d6326fe7188ab1135b7
|
@@ -10,22 +10,27 @@ module CwCardUtils
|
|
10
10
|
|
11
11
|
# Probability of drawing at least one of the target cards
|
12
12
|
def prob_single(target_names, draws)
|
13
|
-
|
14
|
-
|
13
|
+
targets = Array(target_names).uniq
|
14
|
+
draws_clamped = clamp_draws(draws)
|
15
|
+
total_copies = count_copies(targets)
|
16
|
+
total = hypergeometric(@deck_size, draws_clamped).to_f
|
17
|
+
prob = 1 - hypergeometric(@deck_size - total_copies, draws_clamped).to_f / total
|
18
|
+
prob.clamp(0.0, 1.0)
|
15
19
|
end
|
16
20
|
|
17
21
|
# Probability of drawing ALL cards in the targets list (synergy pair/trio)
|
18
22
|
def prob_combo(target_names, draws)
|
19
|
-
|
23
|
+
targets = Array(target_names).uniq
|
24
|
+
case targets.size
|
20
25
|
when 1
|
21
|
-
prob_single(
|
26
|
+
prob_single(targets, draws)
|
22
27
|
when 2
|
23
|
-
prob_two_card_combo(
|
28
|
+
prob_two_card_combo(targets, draws)
|
24
29
|
when 3
|
25
|
-
prob_three_card_combo(
|
30
|
+
prob_three_card_combo(targets, draws)
|
26
31
|
else
|
27
32
|
# For >3 cards, fallback to approximation
|
28
|
-
approx_combo(
|
33
|
+
approx_combo(targets, draws)
|
29
34
|
end
|
30
35
|
end
|
31
36
|
|
@@ -33,60 +38,70 @@ module CwCardUtils
|
|
33
38
|
|
34
39
|
# Exact for 2-card combos
|
35
40
|
def prob_two_card_combo(names, draws)
|
36
|
-
|
37
|
-
copies_b = count_copies([names[1]])
|
41
|
+
draws_clamped = clamp_draws(draws)
|
38
42
|
|
39
|
-
|
43
|
+
copies_a = copies_by_name[names[0]]
|
44
|
+
copies_b = copies_by_name[names[1]]
|
45
|
+
|
46
|
+
total = hypergeometric(@deck_size, draws_clamped).to_f
|
40
47
|
|
41
48
|
# Probability missing A
|
42
|
-
miss_a = hypergeometric(@deck_size - copies_a,
|
49
|
+
miss_a = hypergeometric(@deck_size - copies_a, draws_clamped) / total
|
43
50
|
# Probability missing B
|
44
|
-
miss_b = hypergeometric(@deck_size - copies_b,
|
51
|
+
miss_b = hypergeometric(@deck_size - copies_b, draws_clamped) / total
|
45
52
|
# Probability missing both
|
46
|
-
miss_both = hypergeometric(@deck_size - (copies_a + copies_b),
|
53
|
+
miss_both = hypergeometric(@deck_size - (copies_a + copies_b), draws_clamped) / total
|
47
54
|
|
48
55
|
# Inclusion–exclusion
|
49
|
-
|
56
|
+
prob = 1 - (miss_a + miss_b - miss_both)
|
57
|
+
prob.clamp(0.0, 1.0)
|
50
58
|
end
|
51
59
|
|
52
60
|
# Exact for 3-card combos
|
53
61
|
def prob_three_card_combo(names, draws)
|
54
|
-
|
55
|
-
|
56
|
-
|
62
|
+
draws_clamped = clamp_draws(draws)
|
63
|
+
|
64
|
+
copies_a = copies_by_name[names[0]]
|
65
|
+
copies_b = copies_by_name[names[1]]
|
66
|
+
copies_c = copies_by_name[names[2]]
|
57
67
|
|
58
|
-
total = hypergeometric(@deck_size,
|
68
|
+
total = hypergeometric(@deck_size, draws_clamped).to_f
|
59
69
|
|
60
|
-
miss_a = hypergeometric(@deck_size - copies_a,
|
61
|
-
miss_b = hypergeometric(@deck_size - copies_b,
|
62
|
-
miss_c = hypergeometric(@deck_size - copies_c,
|
70
|
+
miss_a = hypergeometric(@deck_size - copies_a, draws_clamped) / total
|
71
|
+
miss_b = hypergeometric(@deck_size - copies_b, draws_clamped) / total
|
72
|
+
miss_c = hypergeometric(@deck_size - copies_c, draws_clamped) / total
|
63
73
|
|
64
|
-
miss_ab = hypergeometric(@deck_size - (copies_a + copies_b),
|
65
|
-
miss_ac = hypergeometric(@deck_size - (copies_a + copies_c),
|
66
|
-
miss_bc = hypergeometric(@deck_size - (copies_b + copies_c),
|
74
|
+
miss_ab = hypergeometric(@deck_size - (copies_a + copies_b), draws_clamped) / total
|
75
|
+
miss_ac = hypergeometric(@deck_size - (copies_a + copies_c), draws_clamped) / total
|
76
|
+
miss_bc = hypergeometric(@deck_size - (copies_b + copies_c), draws_clamped) / total
|
67
77
|
|
68
|
-
miss_abc = hypergeometric(@deck_size - (copies_a + copies_b + copies_c),
|
78
|
+
miss_abc = hypergeometric(@deck_size - (copies_a + copies_b + copies_c), draws_clamped) / total
|
69
79
|
|
70
80
|
# Inclusion–exclusion for 3 sets
|
71
|
-
|
81
|
+
prob = 1 - (miss_a + miss_b + miss_c) +
|
72
82
|
(miss_ab + miss_ac + miss_bc) -
|
73
|
-
miss_abc
|
83
|
+
miss_abc
|
84
|
+
prob.clamp(0.0, 1.0)
|
74
85
|
end
|
75
86
|
|
76
|
-
# Approximation for >3 cards
|
87
|
+
# Approximation for >3 cards
|
77
88
|
def approx_combo(target_names, draws)
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
89
|
+
draws_clamped = clamp_draws(draws)
|
90
|
+
total = hypergeometric(@deck_size, draws_clamped).to_f
|
91
|
+
|
92
|
+
prob_missing = target_names.sum do |name|
|
93
|
+
copies = copies_by_name[name]
|
94
|
+
hypergeometric(@deck_size - copies, draws_clamped).to_f / total
|
83
95
|
end
|
84
|
-
|
96
|
+
|
97
|
+
prob = 1 - prob_missing
|
98
|
+
prob.clamp(0.0, 1.0)
|
85
99
|
end
|
86
100
|
|
87
101
|
# Utility: count how many copies of given cards are in the deck
|
88
102
|
def count_copies(names)
|
89
|
-
|
103
|
+
unique = names.uniq
|
104
|
+
@deck.main.sum { |card| unique.include?(card.name) ? card.count : 0 }
|
90
105
|
end
|
91
106
|
|
92
107
|
# Hypergeometric combination helper
|
@@ -95,9 +110,19 @@ module CwCardUtils
|
|
95
110
|
factorial(n) / (factorial(k) * factorial(n - k))
|
96
111
|
end
|
97
112
|
|
113
|
+
def clamp_draws(draws)
|
114
|
+
return 0 if draws.to_i < 0
|
115
|
+
[draws.to_i, @deck_size].min
|
116
|
+
end
|
117
|
+
|
98
118
|
def factorial(n)
|
99
119
|
return 1 if n.zero?
|
100
120
|
(1..n).reduce(1, :*)
|
101
121
|
end
|
122
|
+
|
123
|
+
# Only need to calculate this once for the deck passed in.
|
124
|
+
def copies_by_name
|
125
|
+
@copies_by_name ||= @deck.main.each_with_object(Hash.new(0)) { |card, h| h[card.name] += card.count }
|
126
|
+
end
|
102
127
|
end
|
103
128
|
end
|