sorare-rewards 1.0.0.beta8 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9a0830eade606c23536297db4a6ce44271d419163722adebe70f2a2a6f5edf1c
4
- data.tar.gz: 5cf895682f14241b6c6a4d242a3d152927b099f171f49117cbf6ebc71d60c420
3
+ metadata.gz: dd12e11f5b4eb146655a6944d6ffd4b541a3cfe926e38b5bb77e7235d4cc92fd
4
+ data.tar.gz: 12acbcb47c3d6f366855278b96d75ab787414f802cbbfe9c597f98a96867c8ae
5
5
  SHA512:
6
- metadata.gz: 8a52f0aaca38e0cf7b17438caeecbe150b4d843e3355fdfb2cd2840ee2046a2de44ad17e0e066b066eaaa01faf8f77b6961ae1f2a1fabe52d8df5a2a8249ada4
7
- data.tar.gz: 365cd2a82541403d1f4ee0b2ad4cf515abe6e0bcd6a9b20ff3a8e2143ac1583198a90b5f17c85f6991f1a8b821dfe3e0035047a97fe1f69bc742282fbd12bf11
6
+ metadata.gz: 858689cacfc5e20404a485c5500266c613c47ec4838fbf909b7975594a6e1eb59cc96d0e3fa14cdc3cfdf4cea2b0f1ad9cd01adc215d02c0369d3ef02a3a7351
7
+ data.tar.gz: ba9f2aa66b4332ba1611f38a9b7fad80582bc1d03f67530bb356d203d4a71f9405ac353461ea4f6383775a03145adb923ade0762b9f1d89041299f7aa2b27e74
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- sorare-rewards (1.0.0.beta8)
4
+ sorare-rewards (1.1.0)
5
5
  activesupport (~> 6.1)
6
6
  interactor (~> 3.0)
7
7
  json (~> 2)
@@ -23,12 +23,15 @@ GEM
23
23
  i18n (1.8.10)
24
24
  concurrent-ruby (~> 1.0)
25
25
  interactor (3.1.2)
26
+ io-wait (0.1.0)
26
27
  json (2.5.1)
27
28
  minitest (5.14.4)
28
29
  net-http (0.1.1)
29
30
  net-protocol
30
31
  uri
31
- net-protocol (0.1.0)
32
+ net-protocol (0.1.1)
33
+ io-wait
34
+ timeout
32
35
  ostruct (0.2.0)
33
36
  parallel (1.20.1)
34
37
  parser (3.0.1.0)
@@ -67,6 +70,7 @@ GEM
67
70
  rubocop (~> 1.0)
68
71
  rubocop-ast (>= 1.1.0)
69
72
  ruby-progressbar (1.11.0)
73
+ timeout (0.1.1)
70
74
  tzinfo (2.0.4)
71
75
  concurrent-ruby (~> 1.0)
72
76
  unicode-display_width (2.0.0)
data/README.md CHANGED
@@ -1,9 +1,5 @@
1
1
  # Sorare::Rewards
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/sorare/rewards`. To experiment with that code, run `bin/console` for an interactive prompt.
4
-
5
- TODO: Delete this and the text above, and describe your gem
6
-
7
3
  ## Installation
8
4
 
9
5
  Add this line to your application's Gemfile:
@@ -22,13 +18,31 @@ Or install it yourself as:
22
18
 
23
19
  ## Usage
24
20
 
25
- To compute the rewards distribution for a given Game Week :
21
+
22
+ First you need to load a game week
26
23
 
27
24
  ```ruby
28
- Sorare::Rewards::build_rewards(slug)
25
+ game_week = Sorare::Rewards::GameWeek.new(hash: 'QmbwCo1fuhRN1T2DyEQtETefsBH6yCj8h7VjMW5iGfT11Z', salt: 'dce0053984405687efcdf3d1a61ffa67')
29
26
  ```
30
27
 
31
- With `slug` being the Fixture slug.
28
+ Game Week's `hash` and `salt` can be retrieved using the GQL API, the salt is made public after the rewards have been distributed.
29
+
30
+
31
+ To compute the number of rewards for the game week :
32
+
33
+ ```ruby
34
+ # Card rewards :
35
+ game_week.card_allocations
36
+
37
+ # Eth rewards :
38
+ game_week.prize_pools
39
+ ```
40
+
41
+ To get the cards rewarded for a GameWeek :
42
+
43
+ ```ruby
44
+ game_week.cards_picked
45
+ ```
32
46
 
33
47
  ## Development
34
48
 
@@ -1,64 +1,96 @@
1
+ limited: &limited
2
+ tier_0:
3
+ - pct: 0
4
+ - pct: 0
5
+ - pct: 0
6
+ - pct: 0
7
+ - pct: 0.33
8
+ tier_1:
9
+ - pct: 0
10
+ - pct: 0
11
+ - pct: 0
12
+ - pct: 0
13
+ - pct: 0.33
14
+ tier_2:
15
+ - pct: 0
16
+ - pct: 0
17
+ - pct: 0
18
+ - pct: 0
19
+ - pct: 0.8
20
+ tier_3:
21
+ - pct: 0
22
+ - pct: 0
23
+ - pct: 0
24
+ - pct: 0
25
+ - pct: 1
26
+
1
27
  rare: &rare
2
28
  tier_0:
3
- - loop: 0
4
- - loop: 0
5
- - loop: 2
6
- - loop: 1
29
+ - cards_per_loop: 0
30
+ - cards_per_loop: 0
31
+ - cards_per_loop: 2
32
+ - cards_per_loop: 1
7
33
  tier_1:
8
- - loop: 0
9
- - loop: 0
10
- - loop: 2
11
- - loop: 1
34
+ - cards_per_loop: 0
35
+ - cards_per_loop: 0
36
+ - cards_per_loop: 2
37
+ - cards_per_loop: 1
12
38
  tier_2:
13
- - loop: 0
14
- - loop: 0
15
- - loop: 1
16
- - loop: 4
39
+ - cards_per_loop: 0
40
+ - cards_per_loop: 0
41
+ - cards_per_loop: 1
42
+ - cards_per_loop: 4
17
43
  tier_3:
18
- - loop: 0
19
- - loop: 0
20
- - loop: 0
21
- - loop: 1
44
+ - cards_per_loop: 0
45
+ - cards_per_loop: 0
46
+ - cards_per_loop: 0
47
+ - cards_per_loop: 1
22
48
 
23
49
  super_rare: &super_rare
24
50
  tier_0:
25
- - loop: 2
26
- - loop: 1
27
- - loop: 0
28
- - loop: 0
51
+ - cards_per_loop: 2
52
+ - cards_per_loop: 1
53
+ - cards_per_loop: 0
54
+ - cards_per_loop: 0
29
55
  tier_1:
30
- - loop: 1
31
- - loop: 1
32
- - loop: 0
33
- - loop: 0
56
+ - cards_per_loop: 1
57
+ - cards_per_loop: 1
58
+ - cards_per_loop: 0
59
+ - cards_per_loop: 0
34
60
  tier_2:
35
- - loop: 0
36
- - loop: 1
37
- - loop: 0
38
- - loop: 0
61
+ - cards_per_loop: 0
62
+ - cards_per_loop: 1
63
+ - cards_per_loop: 0
64
+ - cards_per_loop: 0
39
65
  tier_3:
40
- - loop: 0
41
- - loop: 1
42
- - loop: 0
43
- - loop: 0
66
+ - cards_per_loop: 0
67
+ - cards_per_loop: 1
68
+ - cards_per_loop: 0
69
+ - cards_per_loop: 0
44
70
 
45
71
  leagues:
46
72
  global-all_star:
73
+ limited: *limited
47
74
  rare: *rare
48
75
  super_rare: *super_rare
49
76
  global-under_twenty_one:
77
+ limited: *limited
50
78
  rare: *rare
51
79
  super_rare: *super_rare
52
80
  champion-europe:
81
+ limited: *limited
53
82
  rare: *rare
54
83
  super_rare: *super_rare
55
84
  challenger-europe:
85
+ limited: *limited
56
86
  rare: *rare
57
87
  super_rare: *super_rare
58
88
  champion-asia:
89
+ limited: *limited
59
90
  rare: *rare
60
91
  super_rare: *super_rare
61
92
  champion-america:
93
+ limited: *limited
62
94
  rare: *rare
63
95
  super_rare: *super_rare
64
96
  special-weekly:
@@ -2,17 +2,20 @@
2
2
 
3
3
  require 'json'
4
4
  require 'net/http'
5
+ require 'active_support/core_ext/module/delegation'
5
6
 
6
7
  module Sorare
7
8
  module Rewards
8
9
  # GameWeek stores the reward data of a game week
9
10
  class GameWeek
10
- attr_reader :data, :config, :salt
11
+ attr_reader :salt
12
+
13
+ delegate :card_allocations, :prize_pools, to: :reward_allocations
14
+ delegate :cards_picked, to: :card_rewards
11
15
 
12
16
  def initialize(data: nil, hash: nil, salt: nil)
13
- uri = URI("#{Sorare::Rewards.configuration.gateway}#{hash}")
14
- @data = data || JSON.parse(::Net::HTTP.get(uri))
15
- @config = GameWeekConfig.new(@data['config'])
17
+ @uri = URI("#{Sorare::Rewards.configuration.gateway}#{hash}")
18
+ @data = data
16
19
  @salt = salt
17
20
  end
18
21
 
@@ -22,6 +25,14 @@ module Sorare
22
25
  end
23
26
  end
24
27
 
28
+ def data
29
+ @data ||= JSON.parse(::Net::HTTP.get(@uri))
30
+ end
31
+
32
+ def config
33
+ @config ||= GameWeekConfig.new(@data['config'])
34
+ end
35
+
25
36
  def supply_for(league, rarity: nil, player: nil)
26
37
  return (supply.dig(league, rarity, player) || 0) if rarity && player
27
38
  return (supply.dig(league, rarity) || {}) if rarity
@@ -37,14 +48,14 @@ module Sorare
37
48
  end
38
49
  end
39
50
 
40
- def rewards
51
+ def reward_allocations
41
52
  raise 'Salt required' unless salt
42
53
 
43
- @rewards ||= Sorare::Rewards::Build.call!(game_week: self, salt: salt)
54
+ @reward_allocations ||= Sorare::Rewards::Build.call!(game_week: self, salt: salt)
44
55
  end
45
56
 
46
57
  def card_rewards
47
- @card_rewards ||= Sorare::Rewards::Pick.call!(rewards.to_h)
58
+ @card_rewards ||= Sorare::Rewards::Pick.call!(reward_allocations.to_h)
48
59
  end
49
60
 
50
61
  def league(name)
@@ -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
@@ -29,7 +29,7 @@ module Sorare
29
29
  allocate_cards
30
30
  allocate_pct if has?('pct')
31
31
  allocate_extra if has?('pct')
32
- allocate_loop if has?('loop')
32
+ allocate_loop if has?('cards_per_loop')
33
33
  end
34
34
 
35
35
  def allocate_divisions(&block)
@@ -55,7 +55,7 @@ module Sorare
55
55
  def allocate_loop
56
56
  while remaining_supply.positive?
57
57
  allocate_divisions do |division_config, division|
58
- allocated[division] += [division_config['loop'] || 0, remaining_supply].min
58
+ allocated[division] += [division_config['cards_per_loop'] || 0, remaining_supply].min
59
59
  end
60
60
  end
61
61
  end
@@ -85,7 +85,7 @@ module Sorare
85
85
  end
86
86
 
87
87
  def check_config!
88
- return if config && [0, 1].include?(config.sum { |q| q['pct'] || 0 })
88
+ return if config
89
89
 
90
90
  context.fail!(error: 'Invalid config')
91
91
  end
@@ -25,13 +25,13 @@ module Sorare
25
25
  delegate :league, :allocations, :pickers, :qualified_players, to: :context
26
26
 
27
27
  def call
28
- context.division_picks = allocations.keys.index_with do |rarity|
28
+ context.cards_picked = allocations.keys.index_with do |rarity|
29
29
  PickForDivisionAndRarity.call!(
30
30
  **context.to_h,
31
31
  rarity: rarity,
32
32
  pickers: pickers[rarity],
33
33
  allocations: allocations[rarity]
34
- ).rarity_picks
34
+ ).cards_picked
35
35
  end
36
36
  end
37
37
  end
@@ -20,7 +20,7 @@ module Sorare
20
20
  delegate :league, :rarity, :allocations, :pickers, to: :context
21
21
 
22
22
  def call
23
- context.rarity_picks = supply!
23
+ context.cards_picked = supply!
24
24
  end
25
25
 
26
26
  def supply!
@@ -28,7 +28,7 @@ module Sorare
28
28
  next [] if pickers&.dig(tier).nil?
29
29
 
30
30
  PickForDivisionRarityAndQuality.call!(**context.to_h, allocations: allocations[tier],
31
- picker: pickers[tier]).picks
31
+ picker: pickers[tier]).cards_picked
32
32
  end
33
33
  end
34
34
  end
@@ -15,11 +15,11 @@ module Sorare
15
15
 
16
16
  MISSING_CARDS = 'There are not enough cards to fulfill the requirements'
17
17
 
18
- delegate :picker, :allocations, :league, :rarity, :force, :picks, to: :context
18
+ delegate :picker, :allocations, :league, :rarity, :force, :cards_picked, to: :context
19
19
  delegate :game_week, to: :league
20
20
 
21
21
  def call
22
- context.picks = []
22
+ context.cards_picked = []
23
23
 
24
24
  pick!
25
25
  check_length! unless force
@@ -27,24 +27,24 @@ module Sorare
27
27
  end
28
28
 
29
29
  def pick!
30
- pick_if_available! while picks.length != allocations && picker.length.positive?
30
+ pick_if_available! while cards_picked.length != allocations && picker.length.positive?
31
31
  end
32
32
 
33
33
  def pick_if_available!
34
34
  player = picker.pick
35
- return if picker.player_counter[player] > game_week.player(player).supply(league, rarity).ceil
35
+ return if picker.player_counter[player] > game_week.player(player).pickable_supply(league, rarity)
36
36
 
37
- picks.push(player)
37
+ cards_picked.push(player)
38
38
  end
39
39
 
40
40
  def reorder!
41
- picks.sort! do |a, b|
41
+ cards_picked.sort! do |a, b|
42
42
  game_week.player(a).rank(league, rarity) <=> game_week.player(b).rank(league, rarity)
43
43
  end
44
44
  end
45
45
 
46
46
  def check_length!
47
- return if picks.length == allocations
47
+ return if cards_picked.length == allocations
48
48
 
49
49
  context.fail!(error: MISSING_CARDS)
50
50
  end
@@ -22,14 +22,14 @@ module Sorare
22
22
  delegate :game_week, :allocations, :salt, :picks, to: :context
23
23
 
24
24
  def call
25
- context.picks = picks
25
+ context.cards_picked = pick!
26
26
  end
27
27
 
28
- def picks
28
+ def pick!
29
29
  allocations.each_league_allocations.each_with_object({}) do |(league_name, league_allocations), picks|
30
30
  picks[league_name] = PickForLeague.call!(
31
31
  **context.to_h, league: game_week.league(league_name), allocations: league_allocations
32
- ).league_picks
32
+ ).cards_picked
33
33
  end
34
34
  end
35
35
  end
@@ -24,10 +24,10 @@ module Sorare
24
24
  context.pickers = {}
25
25
  add_pickers!
26
26
 
27
- context.league_picks = allocations.keys.index_with do |division|
27
+ context.cards_picked = allocations.keys.index_with do |division|
28
28
  PickForDivision.call!(
29
29
  **context.to_h, pickers: pickers, allocations: allocations[division]
30
- ).division_picks
30
+ ).cards_picked
31
31
  end
32
32
  end
33
33
 
@@ -24,11 +24,11 @@ module Sorare
24
24
  # Depending on the way the allocations interpreter is implemented it could be done by him or we
25
25
  # update the allocations process to match the structure
26
26
  def call
27
- context.picks = Sorare::Rewards::Cards::PickForGameWeek.call!(
27
+ context.cards_picked = Sorare::Rewards::Cards::PickForGameWeek.call!(
28
28
  **context.to_h,
29
29
  salt: salt,
30
30
  allocations: transposed_allocations
31
- ).picks
31
+ ).cards_picked
32
32
  end
33
33
 
34
34
  def transposed_allocations
@@ -13,7 +13,7 @@ module Sorare
13
13
 
14
14
  def call
15
15
  context.division_prize_pools = available_prize_pools.map do |available_prize_pool|
16
- available_prize_pool * percentage_rewarded
16
+ (available_prize_pool * percentage_rewarded).round(2)
17
17
  end
18
18
  end
19
19
 
@@ -16,16 +16,14 @@ 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_supply(player) }
27
25
 
28
- rounded_supply(total_supply)
26
+ round_and_cap_supply(total_float_supply)
29
27
  end
30
28
 
31
29
  def distribute_in_tiers!
@@ -43,12 +41,15 @@ module Sorare
43
41
  end
44
42
  end
45
43
 
46
- def player_supply(slug)
47
- game_week.player(slug).supply(league, rarity)
44
+ def round_and_cap_supply(float_supply)
45
+ [
46
+ float_supply.floor + remaining_supply(float_supply.modulo(1)), # Supply limit
47
+ league.cards_in_circulation_limit(rarity) # Circulation limit
48
+ ].compact.min
48
49
  end
49
50
 
50
- def rounded_supply(float_supply)
51
- float_supply.floor + remaining_supply(float_supply.modulo(1))
51
+ def player_supply(slug)
52
+ game_week.player(slug).reward_pool_supply_contribution(league, rarity)
52
53
  end
53
54
 
54
55
  def remaining_supply(reward_probability)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'time'
3
+ require 'active_support/core_ext/module/delegation'
4
4
 
5
5
  module Sorare
6
6
  module Rewards
@@ -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?
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'json'
4
- require 'net/http'
3
+ require 'active_support/core_ext/module/delegation'
5
4
 
6
5
  module Sorare
7
6
  module Rewards
@@ -25,11 +25,18 @@ module Sorare
25
25
  @game_data.present?
26
26
  end
27
27
 
28
- def supply(league, rarity)
28
+ def reward_pool_supply_contribution(league, rarity)
29
29
  return 0 unless playing? && remaining_games_for_supply.positive?
30
30
 
31
31
  [
32
- uncapped_supply_for_game_week(league, rarity), # Remaining supply
32
+ uncapped_supply_for_game_week(league, rarity),
33
+ pickable_supply(league, rarity)
34
+ ].min
35
+ end
36
+
37
+ def pickable_supply(league, rarity)
38
+ [
39
+ overall_supply(league, rarity), # Remaining supply
33
40
  available_during_cooldown(league, rarity), # Cooldown limit
34
41
  @config.distribution_limit(rarity) # Hard limit
35
42
  ].min
@@ -52,10 +59,12 @@ module Sorare
52
59
  end
53
60
 
54
61
  def rewarded_during_cooldown(league, rarity)
55
- (rewards&.dig(league) || []).count { |at| (Time.parse(at) + @config.cooldown_since(rarity)) < Time.now }
62
+ (@game_data&.dig(league.name, rarity) || []).count do |at|
63
+ (Time.parse(at) + @config.cooldown_since(rarity)) > Time.now
64
+ end
56
65
  end
57
66
 
58
- %w[rewards remaining_games_for_supply remaining_games].each do |key|
67
+ %w[remaining_games_for_supply remaining_games].each do |key|
59
68
  define_method(key) do
60
69
  @game_data&.dig(key)
61
70
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Sorare
4
4
  module Rewards
5
- VERSION = '1.0.0.beta8'
5
+ VERSION = '1.1.0'
6
6
  end
7
7
  end
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.0.beta8
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - pierre
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-07-05 00:00:00.000000000 Z
11
+ date: 2021-08-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -220,9 +220,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
220
220
  version: 2.7.1
221
221
  required_rubygems_version: !ruby/object:Gem::Requirement
222
222
  requirements:
223
- - - ">"
223
+ - - ">="
224
224
  - !ruby/object:Gem::Version
225
- version: 1.3.1
225
+ version: '0'
226
226
  requirements: []
227
227
  rubygems_version: 3.1.6
228
228
  signing_key: