sorare-rewards 1.0.2 → 1.1.3.beta0

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: 503bd2bc5d9b16f6c2ba4e9209c14920dc000730dc3466eea88534abe97187c2
4
- data.tar.gz: c03342737d6af15c514932029928fb26acd255700811e8bd9de53f62f7a5a91e
3
+ metadata.gz: bcb36662a381adcf213a5ab17ad97ae1d57f862f84e274679657a05d096476cb
4
+ data.tar.gz: 1d38e28d7fc1912cf14ae3dfa48546f1aa87b427e5025ac8e2896dd3dce6a117
5
5
  SHA512:
6
- metadata.gz: 496c0f02f3c3735895c314d5f680a36fd3a2965a63d196f209efdd6cefcb6c9eaf5a8f5995c90a27ad36963cd68eb071f5467abf998738b856ca1a389dba87c6
7
- data.tar.gz: 5386e7e1e7f95c9b703882e5623c0aeec9ce71b4d9d46e85f807601e2f918b76c095dbac01f26ccfacd2262a0384d4f83ec22857810629e5b29ff274bc6a2d66
6
+ metadata.gz: e66851acb6b951557c607e2ebf24552ea5b16d63d3faf260f344175bf674758dc5efe7549b96c6fa164f1bdfc263066234b0e09f79a66ccbbb1c5f7dc0df903e
7
+ data.tar.gz: 6a2a8979952b7446ac9470b360fae8e7c2d8c53dd4db4442da9e60472090c2bae881fcf625e9c5da5a19d8bcff9c0356f80c2acb3a1586b074bdeb87f2e0689c
data/Gemfile.lock CHANGED
@@ -1,30 +1,30 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- sorare-rewards (1.0.2)
5
- activesupport (~> 6.1)
6
- interactor (~> 3.0)
4
+ sorare-rewards (1.1.3.beta0)
5
+ activesupport (~> 6.1.4)
6
+ interactor (~> 3.1.2)
7
7
  json (~> 2)
8
8
  net-http (~> 0.1.1)
9
- yaml (~> 0.1.1)
9
+ yaml (~> 0.2.0)
10
10
 
11
11
  GEM
12
12
  remote: https://rubygems.org/
13
13
  specs:
14
- activesupport (6.1.3.2)
14
+ activesupport (6.1.4.1)
15
15
  concurrent-ruby (~> 1.0, >= 1.0.2)
16
16
  i18n (>= 1.6, < 2)
17
17
  minitest (>= 5.1)
18
18
  tzinfo (~> 2.0)
19
19
  zeitwerk (~> 2.3)
20
20
  ast (2.4.2)
21
- concurrent-ruby (1.1.8)
21
+ concurrent-ruby (1.1.9)
22
22
  diff-lcs (1.4.4)
23
23
  i18n (1.8.10)
24
24
  concurrent-ruby (~> 1.0)
25
25
  interactor (3.1.2)
26
26
  io-wait (0.1.0)
27
- json (2.5.1)
27
+ json (2.6.0)
28
28
  minitest (5.14.4)
29
29
  net-http (0.1.1)
30
30
  net-protocol
@@ -32,9 +32,9 @@ GEM
32
32
  net-protocol (0.1.1)
33
33
  io-wait
34
34
  timeout
35
- ostruct (0.2.0)
36
- parallel (1.20.1)
37
- parser (3.0.1.0)
35
+ ostruct (0.4.0)
36
+ parallel (1.21.0)
37
+ parser (3.0.2.0)
38
38
  ast (~> 2.4.1)
39
39
  rainbow (3.0.0)
40
40
  rake (12.3.3)
@@ -53,42 +53,41 @@ GEM
53
53
  diff-lcs (>= 1.2.0, < 2.0)
54
54
  rspec-support (~> 3.10.0)
55
55
  rspec-support (3.10.2)
56
- rubocop (1.12.1)
56
+ rubocop (1.22.1)
57
57
  parallel (~> 1.10)
58
58
  parser (>= 3.0.0.0)
59
59
  rainbow (>= 2.2.2, < 4.0)
60
60
  regexp_parser (>= 1.8, < 3.0)
61
61
  rexml
62
- rubocop-ast (>= 1.2.0, < 2.0)
62
+ rubocop-ast (>= 1.12.0, < 2.0)
63
63
  ruby-progressbar (~> 1.7)
64
64
  unicode-display_width (>= 1.4.0, < 3.0)
65
- rubocop-ast (1.4.1)
66
- parser (>= 2.7.1.5)
67
- rubocop-rake (0.5.1)
68
- rubocop
69
- rubocop-rspec (2.2.0)
65
+ rubocop-ast (1.12.0)
66
+ parser (>= 3.0.1.1)
67
+ rubocop-rake (0.6.0)
70
68
  rubocop (~> 1.0)
71
- rubocop-ast (>= 1.1.0)
69
+ rubocop-rspec (2.5.0)
70
+ rubocop (~> 1.19)
72
71
  ruby-progressbar (1.11.0)
73
- timeout (0.1.1)
72
+ timeout (0.2.0)
74
73
  tzinfo (2.0.4)
75
74
  concurrent-ruby (~> 1.0)
76
- unicode-display_width (2.0.0)
75
+ unicode-display_width (2.1.0)
77
76
  uri (0.10.1)
78
- yaml (0.1.1)
79
- zeitwerk (2.4.2)
77
+ yaml (0.2.0)
78
+ zeitwerk (2.5.0)
80
79
 
81
80
  PLATFORMS
82
81
  ruby
83
82
 
84
83
  DEPENDENCIES
85
- ostruct (~> 0.2.0)
84
+ ostruct (~> 0.4.0)
86
85
  rake (~> 12.0)
87
86
  rspec (~> 3.10)
88
- rubocop (~> 1.12.1)
89
- rubocop-rake (~> 0.5.1)
90
- rubocop-rspec (~> 2.2.0)
87
+ rubocop (~> 1.22.1)
88
+ rubocop-rake (~> 0.6.0)
89
+ rubocop-rspec (~> 2.5.0)
91
90
  sorare-rewards!
92
91
 
93
92
  BUNDLED WITH
94
- 2.1.4
93
+ 2.2.22
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+
5
+ module Sorare
6
+ module Rewards
7
+ # WithAnalytics defines analytics methods
8
+ module WithAnalytics
9
+ extend ActiveSupport::Concern
10
+
11
+ def analytics
12
+ @analytics ||= each_league.each_with_object({}) do |league, stats|
13
+ stats[league.name] = league_analytics(league)
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def reward_distributed
20
+ @reward_distributed ||= reward_allocations.card_allocations.transform_values do |league|
21
+ league.transform_values do |rarity|
22
+ rarity.sum { |_, divisions| divisions.values.sum }
23
+ end
24
+ end
25
+ end
26
+
27
+ def league_analytics(league)
28
+ {
29
+ playing_players_with_supply: nb_of_playing_players(league),
30
+ available_cards: nb_of_available_cards(league),
31
+ cooldown_players: nb_of_fully_cooldown_players(league),
32
+ rewarded: reward_distributed[league.name]
33
+ }
34
+ end
35
+
36
+ def nb_of_playing_players(league)
37
+ supply_for(league.name).keys.index_with do |rarity|
38
+ (supply_for(league.name, rarity: rarity).keys & playing_players.keys).count
39
+ end
40
+ end
41
+
42
+ def nb_of_available_cards(league)
43
+ supply_for(league.name).keys.index_with do |rarity|
44
+ supply_for(league.name, rarity: rarity).sum { |_, data| data['supply'] }
45
+ end
46
+ end
47
+
48
+ def nb_of_fully_cooldown_players(league)
49
+ supply_for(league.name).keys.index_with do |rarity|
50
+ supply_for(league.name, rarity: rarity).filter do |player_slug|
51
+ player(player_slug).available_during_cooldown(league, rarity).zero?
52
+ end.count
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -8,6 +8,8 @@ module Sorare
8
8
  module Rewards
9
9
  # GameWeek stores the reward data of a game week
10
10
  class GameWeek
11
+ include WithAnalytics
12
+
11
13
  attr_reader :salt
12
14
 
13
15
  delegate :card_allocations, :prize_pools, to: :reward_allocations
@@ -69,6 +71,10 @@ module Sorare
69
71
  def player(slug)
70
72
  Player.new(slug, self)
71
73
  end
74
+
75
+ def inspect
76
+ super.splice(:version)
77
+ end
72
78
  end
73
79
  end
74
80
  end
@@ -12,7 +12,7 @@ module Sorare
12
12
  @config = config
13
13
  end
14
14
 
15
- %w[cooldown limits prize_pool_currency].each do |key|
15
+ %w[cooldown limits prize_pool_currency rewards_ratio_per_cards_in_circulation].each do |key|
16
16
  define_method(key) do
17
17
  config[key]
18
18
  end
@@ -17,10 +17,8 @@ module Sorare
17
17
  end
18
18
 
19
19
  def allocate_and_format!
20
- allocations = allocate!
21
-
22
- allocations.keys.index_with do |rarity|
23
- allocations[rarity].first.keys.index_with do |division|
20
+ each_rarity_allocation do |rarity|
21
+ (allocations[rarity].first || {}).keys.index_with do |division|
24
22
  allocations[rarity].each_with_index.map do |qualified_allocations, tier|
25
23
  [Sorare::Rewards.configuration.transform_tier.call(tier), qualified_allocations[division]]
26
24
  end.to_h
@@ -28,8 +26,12 @@ module Sorare
28
26
  end
29
27
  end
30
28
 
31
- def allocate!
32
- (config || {}).keys.index_with do |rarity|
29
+ def each_rarity_allocation(&block)
30
+ allocations.keys.index_with(&block)
31
+ end
32
+
33
+ def allocations
34
+ @allocations ||= (config || {}).keys.index_with do |rarity|
33
35
  ComputeForRarity.call!(**context.to_h, supply: supply[rarity], config: config[rarity]).rarity_allocations
34
36
  end
35
37
  end
@@ -15,7 +15,13 @@ module Sorare
15
15
  def call
16
16
  context.fail!(error: 'Invalid config') unless config
17
17
 
18
- context.rarity_allocations = supply.each_with_index.map do |tier_supply, tier|
18
+ context.rarity_allocations = allocations
19
+ end
20
+
21
+ def allocations
22
+ return [] unless supply
23
+
24
+ supply.each_with_index.map do |tier_supply, tier|
19
25
  ComputeForQuality.call!(
20
26
  **context.to_h,
21
27
  supply: tier_supply,
@@ -16,39 +16,49 @@ module Sorare
16
16
  def call
17
17
  context.rarity_supply = []
18
18
 
19
- context.rewardable = compute_rewardable_supply!
19
+ context.rewardable = rewardable_supply
20
20
  distribute_in_tiers!
21
21
  end
22
22
 
23
- def compute_rewardable_supply!
24
- total_supply = supply.keys.sum do |player|
25
- player_supply(player)
26
- end
23
+ def rewardable_supply
24
+ total_float_supply = supply.keys.sum { |player| player_contribution(player) }
27
25
 
28
- rounded_supply(total_supply)
26
+ round_and_cap_league_supply(total_float_supply)
29
27
  end
30
28
 
31
29
  def distribute_in_tiers!
32
30
  ctx = Tiers::QualifySupply.call!(sorted_supply: supply)
33
- ctx.supply.each do |tier_supply|
31
+ qualified_players = Tiers::QualifyPlayers.call!(sorted_supply: supply).players
32
+ ctx.supply.each_with_index do |tier_supply, index|
34
33
  rarity_supply.push(
35
- ComputeForQuality.call!(
36
- randomizer: randomizer,
37
- total_supply: ctx.count,
38
- tier_supply: tier_supply,
39
- rewardable: rewardable,
40
- rewarded: rarity_supply
41
- ).quality_supply
34
+ capped_tier_supply(ctx.count, tier_supply, rewardable, qualified_players[index])
42
35
  )
43
36
  end
44
37
  end
45
38
 
46
- def player_supply(slug)
39
+ # Cap with the actual pickable supply for the tier
40
+ def capped_tier_supply(total_supply, tier_supply, rewardable, tier_players)
41
+ target = ComputeForQuality.call!(
42
+ randomizer: randomizer, total_supply: total_supply,
43
+ tier_supply: tier_supply, rewardable: rewardable, rewarded: rarity_supply
44
+ ).quality_supply
45
+
46
+ [target, tier_players.sum { |player| player_pickable_supply(player) }].min
47
+ end
48
+
49
+ def round_and_cap_league_supply(float_supply)
50
+ [
51
+ float_supply.floor + remaining_supply(float_supply.modulo(1)), # Supply limit
52
+ league.cards_in_circulation_limit(rarity) # Circulation limit
53
+ ].compact.min
54
+ end
55
+
56
+ def player_contribution(slug)
47
57
  game_week.player(slug).reward_pool_supply_contribution(league, rarity)
48
58
  end
49
59
 
50
- def rounded_supply(float_supply)
51
- float_supply.floor + remaining_supply(float_supply.modulo(1))
60
+ def player_pickable_supply(slug)
61
+ game_week.player(slug).pickable_supply(league, rarity)
52
62
  end
53
63
 
54
64
  def remaining_supply(reward_probability)
@@ -33,13 +33,13 @@ module Sorare
33
33
  end
34
34
 
35
35
  def tier_depth
36
- @tier_depth ||= sorted_supply.keys.length / (nb_of_tiers**2 - 1)
36
+ @tier_depth ||= sorted_supply.keys.length / ((nb_of_tiers**2) - 1)
37
37
  end
38
38
 
39
39
  def nb_of_tiers
40
40
  @nb_of_tiers ||= begin
41
41
  default = Sorare::Rewards.configuration.tiers
42
- default -= 1 while (default**2 - 1) > sorted_supply.keys.length
42
+ default -= 1 while ((default**2) - 1) > sorted_supply.keys.length
43
43
 
44
44
  default
45
45
  end
@@ -49,7 +49,7 @@ module Sorare
49
49
  return 0 unless tier.positive?
50
50
  return sorted_supply.keys.length if tier == nb_of_tiers
51
51
 
52
- tier_index(tier - 1) + tier_depth * tier_size(tier - 1)
52
+ tier_index(tier - 1) + (tier_depth * tier_size(tier - 1))
53
53
  end
54
54
 
55
55
  def tier_size(tier)
@@ -17,12 +17,18 @@ module Sorare
17
17
  @game_week = game_week
18
18
  end
19
19
 
20
- %w[playing_rate prize_pools].each do |key|
20
+ %w[playing_rate prize_pools cards_in_circulation].each do |key|
21
21
  define_method(key) do
22
22
  config[key]
23
23
  end
24
24
  end
25
25
 
26
+ def cards_in_circulation_limit(rarity)
27
+ return unless cards_in_circulation&.dig(rarity) && game_week.config.rewards_ratio_per_cards_in_circulation
28
+
29
+ (cards_in_circulation[rarity] * game_week.config.rewards_ratio_per_cards_in_circulation).floor
30
+ end
31
+
26
32
  def each_division_prize_pools
27
33
  prize_pools.each_with_index do |prize_pool, index|
28
34
  return to_enum(:each_division_prize_pools) unless block_given?
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Sorare
4
4
  module Rewards
5
- VERSION = '1.0.2'
5
+ VERSION = '1.1.3.beta0'
6
6
  end
7
7
  end
@@ -30,15 +30,15 @@ Gem::Specification.new do |spec|
30
30
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
31
  spec.require_paths = ['lib']
32
32
 
33
- spec.add_dependency 'activesupport', '~> 6.1'
34
- spec.add_dependency 'interactor', '~> 3.0'
33
+ spec.add_dependency 'activesupport', '~> 6.1.4'
34
+ spec.add_dependency 'interactor', '~> 3.1.2'
35
35
  spec.add_dependency 'json', '~> 2'
36
36
  spec.add_dependency 'net-http', '~> 0.1.1'
37
- spec.add_dependency 'yaml', '~> 0.1.1'
38
- spec.add_development_dependency 'ostruct', '~> 0.2.0'
37
+ spec.add_dependency 'yaml', '~> 0.2.0'
38
+ spec.add_development_dependency 'ostruct', '~> 0.4.0'
39
39
  spec.add_development_dependency 'rspec', '~> 3.10'
40
- spec.add_development_dependency 'rubocop', '~> 1.12.1'
41
- spec.add_development_dependency 'rubocop-rake', '~> 0.5.1'
42
- spec.add_development_dependency 'rubocop-rspec', '~> 2.2.0'
40
+ spec.add_development_dependency 'rubocop', '~> 1.22.1'
41
+ spec.add_development_dependency 'rubocop-rake', '~> 0.6.0'
42
+ spec.add_development_dependency 'rubocop-rspec', '~> 2.5.0'
43
43
  end
44
44
  # rubocop:enable Metrics/BlockLength
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sorare-rewards
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.1.3.beta0
5
5
  platform: ruby
6
6
  authors:
7
7
  - pierre
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-08-17 00:00:00.000000000 Z
11
+ date: 2021-10-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '6.1'
19
+ version: 6.1.4
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '6.1'
26
+ version: 6.1.4
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: interactor
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '3.0'
33
+ version: 3.1.2
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '3.0'
40
+ version: 3.1.2
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: json
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -72,28 +72,28 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 0.1.1
75
+ version: 0.2.0
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 0.1.1
82
+ version: 0.2.0
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: ostruct
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: 0.2.0
89
+ version: 0.4.0
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: 0.2.0
96
+ version: 0.4.0
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: rspec
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -114,42 +114,42 @@ dependencies:
114
114
  requirements:
115
115
  - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: 1.12.1
117
+ version: 1.22.1
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
- version: 1.12.1
124
+ version: 1.22.1
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: rubocop-rake
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
129
  - - "~>"
130
130
  - !ruby/object:Gem::Version
131
- version: 0.5.1
131
+ version: 0.6.0
132
132
  type: :development
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: 0.5.1
138
+ version: 0.6.0
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: rubocop-rspec
141
141
  requirement: !ruby/object:Gem::Requirement
142
142
  requirements:
143
143
  - - "~>"
144
144
  - !ruby/object:Gem::Version
145
- version: 2.2.0
145
+ version: 2.5.0
146
146
  type: :development
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
150
  - - "~>"
151
151
  - !ruby/object:Gem::Version
152
- version: 2.2.0
152
+ version: 2.5.0
153
153
  description:
154
154
  email:
155
155
  - pierre@sorare.com
@@ -171,6 +171,7 @@ files:
171
171
  - data.patch
172
172
  - lib/sorare/rewards.rb
173
173
  - lib/sorare/rewards/allocation_configuration.yml
174
+ - lib/sorare/rewards/concerns/with_analytics.rb
174
175
  - lib/sorare/rewards/configuration.rb
175
176
  - lib/sorare/rewards/game_week.rb
176
177
  - lib/sorare/rewards/game_week_allocations.rb
@@ -220,11 +221,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
220
221
  version: 2.7.1
221
222
  required_rubygems_version: !ruby/object:Gem::Requirement
222
223
  requirements:
223
- - - ">="
224
+ - - ">"
224
225
  - !ruby/object:Gem::Version
225
- version: '0'
226
+ version: 1.3.1
226
227
  requirements: []
227
- rubygems_version: 3.1.6
228
+ rubygems_version: 3.2.22
228
229
  signing_key:
229
230
  specification_version: 4
230
231
  summary: '["Sorare", "reward", "allocation", "algorithm"]'