sorare-rewards 0.1.0.beta11 → 1.0.0.beta5
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/Gemfile.lock +1 -1
- data/data.patch +0 -0
- data/lib/sorare/rewards.rb +0 -9
- data/lib/sorare/rewards/configuration.rb +7 -5
- data/lib/sorare/rewards/game_week.rb +63 -0
- data/lib/sorare/rewards/game_week_allocations.rb +32 -0
- data/lib/sorare/rewards/game_week_config.rb +38 -0
- data/lib/sorare/rewards/interactor.rb +24 -0
- data/lib/sorare/rewards/interactors/allocations/compute_for_game_week.rb +0 -1
- data/lib/sorare/rewards/interactors/allocations/compute_for_league.rb +0 -1
- data/lib/sorare/rewards/interactors/allocations/compute_for_quality.rb +0 -1
- data/lib/sorare/rewards/interactors/allocations/compute_for_rarity.rb +0 -1
- data/lib/sorare/rewards/interactors/build.rb +12 -10
- data/lib/sorare/rewards/interactors/cards/pick_for_division.rb +8 -12
- data/lib/sorare/rewards/interactors/cards/pick_for_division_and_rarity.rb +7 -7
- data/lib/sorare/rewards/interactors/cards/pick_for_division_rarity_and_quality.rb +15 -10
- data/lib/sorare/rewards/interactors/cards/pick_for_game_week.rb +6 -8
- data/lib/sorare/rewards/interactors/cards/pick_for_league.rb +22 -22
- data/lib/sorare/rewards/interactors/pick.rb +8 -3
- data/lib/sorare/rewards/interactors/prize_pools/compute_for_division.rb +32 -0
- data/lib/sorare/rewards/interactors/prize_pools/compute_for_game_week.rb +27 -0
- data/lib/sorare/rewards/interactors/prize_pools/compute_for_league.rb +30 -0
- data/lib/sorare/rewards/interactors/supply/compute_for_game_week.rb +16 -5
- data/lib/sorare/rewards/interactors/supply/compute_for_quality.rb +0 -2
- data/lib/sorare/rewards/interactors/supply/compute_for_rarity.rb +4 -14
- data/lib/sorare/rewards/interactors/tiers/qualify_players.rb +2 -3
- data/lib/sorare/rewards/interactors/tiers/qualify_supply.rb +0 -1
- data/lib/sorare/rewards/league.rb +55 -0
- data/lib/sorare/rewards/picker.rb +41 -0
- data/lib/sorare/rewards/player.rb +77 -0
- data/lib/sorare/rewards/prize_pool_configuration.yml +9 -0
- data/lib/sorare/rewards/version.rb +1 -1
- metadata +14 -3
- data/lib/sorare/rewards/interactors/supply/compute_for_league.rb +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0a70b22851cb74e28254ed02396bcb2663b0af657ea7b91ab95517d8be764e27
|
4
|
+
data.tar.gz: 8bee3c9c69e8e7cbf16ad0f612b9cbbde502582cc657e4b952ded56e6c11fce5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9ccfd7fa498eb3c7795a0c658a6921703bcd25bd536f3742aac1492e7d7e0963bb6828b4ed3836e40aab6d1844542353c137c447e461af3ca88da3eb67969936
|
7
|
+
data.tar.gz: 6baec2fd1cec6cbdb0c3493c64169afabe0d07df70920b6f8a8a253a8a330053feed8a1a03ed62e0d4034b6aab601a4fe90dd8a213c514fdf77bf56fddf2d59e
|
data/Gemfile.lock
CHANGED
data/data.patch
ADDED
File without changes
|
data/lib/sorare/rewards.rb
CHANGED
@@ -21,15 +21,6 @@ module Sorare
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
def self.game_week_data(hash)
|
25
|
-
uri = URI("#{configuration.gateway}#{hash}")
|
26
|
-
JSON.parse(::Net::HTTP.get(uri))
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.build_rewards(salt:, hash: nil, data: nil)
|
30
|
-
Sorare::Rewards::Build.call!(data: data || game_week_data(hash), salt: salt).allocations
|
31
|
-
end
|
32
|
-
|
33
24
|
def self.pick(salt:, hash: nil, data: nil)
|
34
25
|
allocation_ctx = Sorare::Rewards::Build.call!(data: data || game_week_data(hash), salt: salt)
|
35
26
|
Sorare::Rewards::Pick.call!(allocation_ctx.to_h).picks
|
@@ -6,19 +6,21 @@ module Sorare
|
|
6
6
|
module Rewards
|
7
7
|
# Configuration stores the runtime configuration
|
8
8
|
class Configuration
|
9
|
-
attr_accessor :tiers, :gateway, :transform_tier, :transform_division,
|
9
|
+
attr_accessor :tiers, :gateway, :transform_tier, :transform_division,
|
10
|
+
:allocation_configuration, :prize_pool_configuration
|
10
11
|
|
11
12
|
def initialize
|
12
13
|
@tiers = 4
|
13
14
|
@gateway = 'https://gateway.pinata.cloud/ipfs/'
|
14
15
|
@transform_tier = ->(tier) { "tier_#{tier}" }
|
15
16
|
@transform_division = ->(division) { "D#{division}" }
|
16
|
-
@allocation_configuration =
|
17
|
+
@allocation_configuration = load_yaml_configuration_file('allocation_configuration.yml')['leagues']
|
18
|
+
@prize_pool_configuration = load_yaml_configuration_file('prize_pool_configuration.yml')['rates']
|
17
19
|
end
|
18
20
|
|
19
|
-
def
|
20
|
-
YAML.safe_load(File.read("#{File.dirname(__FILE__)}
|
21
|
-
.with_indifferent_access
|
21
|
+
def load_yaml_configuration_file(filename)
|
22
|
+
YAML.safe_load(File.read("#{File.dirname(__FILE__)}/#{filename}"), [], [], true)
|
23
|
+
.with_indifferent_access
|
22
24
|
end
|
23
25
|
end
|
24
26
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'net/http'
|
5
|
+
|
6
|
+
module Sorare
|
7
|
+
module Rewards
|
8
|
+
# GameWeek stores the reward data of a game week
|
9
|
+
class GameWeek
|
10
|
+
attr_reader :data, :config, :salt
|
11
|
+
|
12
|
+
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'])
|
16
|
+
@salt = salt
|
17
|
+
end
|
18
|
+
|
19
|
+
%w[public_seed version supply playing_players leagues].each do |key|
|
20
|
+
define_method(key) do
|
21
|
+
data[key]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def supply_for(league, rarity: nil, player: nil)
|
26
|
+
return (supply.dig(league, rarity, player) || 0) if rarity && player
|
27
|
+
return (supply.dig(league, rarity) || {}) if rarity
|
28
|
+
|
29
|
+
supply[league] || {}
|
30
|
+
end
|
31
|
+
|
32
|
+
def each_league
|
33
|
+
(leagues || {}).each_key do |league_name|
|
34
|
+
return to_enum(:each_league) unless block_given?
|
35
|
+
|
36
|
+
yield(league(league_name))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def rewards
|
41
|
+
raise 'Salt required' unless salt
|
42
|
+
|
43
|
+
@rewards ||= Sorare::Rewards::Build.call!(game_week: self, salt: salt)
|
44
|
+
end
|
45
|
+
|
46
|
+
def card_rewards
|
47
|
+
@card_rewards ||= Sorare::Rewards::Pick.call!(rewards.to_h)
|
48
|
+
end
|
49
|
+
|
50
|
+
def league(name)
|
51
|
+
raise 'Unknown league' unless (leagues || {})[name]
|
52
|
+
|
53
|
+
League.new(
|
54
|
+
game_week: self, name: name, config: leagues[name], supply: supply[leagues.dig(name, 'pool_supply')]
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
def player(slug)
|
59
|
+
Player.new(slug, self)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sorare
|
4
|
+
module Rewards
|
5
|
+
# GameWeekAllocations stores the expected allocations for a game week
|
6
|
+
class GameWeekAllocations
|
7
|
+
attr_reader :allocations
|
8
|
+
|
9
|
+
def initialize(allocations)
|
10
|
+
@allocations = allocations
|
11
|
+
end
|
12
|
+
|
13
|
+
def each_league_allocations
|
14
|
+
allocations.each_key do |league_name|
|
15
|
+
return to_enum(:each_league_allocations) unless block_given?
|
16
|
+
|
17
|
+
yield(league_name, allocations[league_name])
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def each_league_rarity_allocations
|
22
|
+
each_league_allocations do |league_name, league_allocations|
|
23
|
+
league_allocations.each_key do |rarity|
|
24
|
+
return to_enum(:each_league_rarity_allocations) unless block_given?
|
25
|
+
|
26
|
+
yield(league_name, rarity, league_allocations[rarity])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
module Sorare
|
6
|
+
module Rewards
|
7
|
+
# GameWeekConfig stores the config used to reward a game week
|
8
|
+
class GameWeekConfig
|
9
|
+
attr_reader :config
|
10
|
+
|
11
|
+
def initialize(config)
|
12
|
+
@config = config
|
13
|
+
end
|
14
|
+
|
15
|
+
%w[cooldown limits prize_pool_currency].each do |key|
|
16
|
+
define_method(key) do
|
17
|
+
config[key]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def over_limit?(rarity, rewarded)
|
22
|
+
limits[rarity] <= rewarded
|
23
|
+
end
|
24
|
+
|
25
|
+
def distribution_limit(rarity)
|
26
|
+
limits[rarity] || 1
|
27
|
+
end
|
28
|
+
|
29
|
+
def cooldown_limit(rarity)
|
30
|
+
cooldown.dig(rarity, 'quantity')
|
31
|
+
end
|
32
|
+
|
33
|
+
def cooldown_since(rarity)
|
34
|
+
cooldown.dig(rarity, 'since')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/concern'
|
4
|
+
require 'interactor'
|
5
|
+
|
6
|
+
module Sorare
|
7
|
+
module Rewards
|
8
|
+
# Interactor extends Interactor to improve on it
|
9
|
+
module Interactor
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
included do
|
13
|
+
include ::Interactor
|
14
|
+
|
15
|
+
# make sure we fail the current context when a call! to another interactor fails
|
16
|
+
around do |interactor|
|
17
|
+
interactor.call
|
18
|
+
rescue ::Interactor::Failure => e
|
19
|
+
context.fail!(error: e.context.error)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -1,9 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'active_support/core_ext/enumerable'
|
4
|
-
require 'active_support/core_ext/hash/indifferent_access'
|
5
3
|
require 'active_support/core_ext/module/delegation'
|
6
|
-
require 'interactor'
|
7
4
|
|
8
5
|
module Sorare
|
9
6
|
module Rewards
|
@@ -11,22 +8,27 @@ module Sorare
|
|
11
8
|
class Build
|
12
9
|
include Interactor
|
13
10
|
|
14
|
-
delegate :
|
11
|
+
delegate :game_week, :salt, to: :context
|
15
12
|
|
16
13
|
def call
|
17
|
-
context.
|
18
|
-
context.
|
14
|
+
context.card_allocations = allocate!
|
15
|
+
context.prize_pools = prize_pools!
|
16
|
+
context.prize_pool_currency = game_week.config.prize_pool_currency
|
17
|
+
end
|
18
|
+
|
19
|
+
def game_week_supply
|
20
|
+
@game_week_supply ||= Supply::ComputeForGameWeek.call!(game_week: game_week, salt: salt)
|
21
|
+
.game_week_supply
|
19
22
|
end
|
20
23
|
|
21
24
|
def allocate!
|
22
25
|
Allocations::ComputeForGameWeek.call!(
|
23
|
-
|
26
|
+
game_week: game_week, supply: game_week_supply, salt: salt
|
24
27
|
).game_week_allocations
|
25
28
|
end
|
26
29
|
|
27
|
-
def
|
28
|
-
|
29
|
-
.game_week_supply
|
30
|
+
def prize_pools!
|
31
|
+
PrizePools::ComputeForGameWeek.call!(game_week: game_week, salt: salt).prize_pools
|
30
32
|
end
|
31
33
|
end
|
32
34
|
end
|
@@ -2,23 +2,16 @@
|
|
2
2
|
|
3
3
|
require 'active_support/core_ext/module/delegation'
|
4
4
|
require 'active_support/core_ext/enumerable'
|
5
|
-
require 'interactor'
|
6
5
|
require 'yaml'
|
7
6
|
|
8
7
|
module Sorare
|
9
8
|
module Rewards
|
10
9
|
module Cards
|
11
10
|
# PickForDivision picks the rewards for a given division
|
12
|
-
# Receive a list of
|
11
|
+
# Receive a list of pickers for each tiers and quality
|
13
12
|
# {
|
14
|
-
# 'rare' => {
|
15
|
-
#
|
16
|
-
# }
|
17
|
-
# }
|
18
|
-
# A game week league supply
|
19
|
-
# {
|
20
|
-
# 'rare' => { 'kylian-mbappe-lottin' => { 'rank' => 1 }, ...},
|
21
|
-
# 'super_rare' => { 'kylian-mbappe-lottin' => { 'rank' => 1 }, ...}
|
13
|
+
# 'rare' => { 'tier_0' => Picker, 'tier_1' => Picker },
|
14
|
+
# 'super_rare' => { 'tier_0' => Picker, 'tier_1' => Picker }
|
22
15
|
# }
|
23
16
|
# And a reward allocations
|
24
17
|
# {
|
@@ -29,12 +22,15 @@ module Sorare
|
|
29
22
|
class PickForDivision
|
30
23
|
include Interactor
|
31
24
|
|
32
|
-
delegate :
|
25
|
+
delegate :league, :allocations, :pickers, :qualified_players, to: :context
|
33
26
|
|
34
27
|
def call
|
35
28
|
context.division_picks = allocations.keys.index_with do |rarity|
|
36
29
|
PickForDivisionAndRarity.call!(
|
37
|
-
**context.to_h,
|
30
|
+
**context.to_h,
|
31
|
+
rarity: rarity,
|
32
|
+
pickers: pickers[rarity],
|
33
|
+
allocations: allocations[rarity]
|
38
34
|
).rarity_picks
|
39
35
|
end
|
40
36
|
end
|
@@ -2,24 +2,22 @@
|
|
2
2
|
|
3
3
|
require 'active_support/core_ext/module/delegation'
|
4
4
|
require 'active_support/core_ext/enumerable'
|
5
|
-
require 'interactor'
|
6
5
|
|
7
6
|
module Sorare
|
8
7
|
module Rewards
|
9
8
|
module Cards
|
10
9
|
# PickForDivisionAndRarity picks the rewards for a given division and rarity
|
11
|
-
# Receive a
|
10
|
+
# Receive a list of pickers for each tiers
|
12
11
|
# {
|
13
|
-
# 'tier_0' =>
|
12
|
+
# 'tier_0' => Picker, 'tier_1' => Picker
|
14
13
|
# }
|
15
|
-
# A game week
|
16
|
-
# { 'kylian-mbappe-lottin' => { 'rank' => 1 }, ...}
|
14
|
+
# A game week data
|
17
15
|
# And a reward allocations
|
18
16
|
# { 'tier_0' => 1, 'tier_1' => 2 }
|
19
17
|
class PickForDivisionAndRarity
|
20
18
|
include Interactor
|
21
19
|
|
22
|
-
delegate :allocations, :
|
20
|
+
delegate :league, :rarity, :allocations, :pickers, to: :context
|
23
21
|
|
24
22
|
def call
|
25
23
|
context.rarity_picks = supply!
|
@@ -27,8 +25,10 @@ module Sorare
|
|
27
25
|
|
28
26
|
def supply!
|
29
27
|
allocations.keys.index_with do |tier|
|
28
|
+
next [] if pickers&.dig(tier).nil?
|
29
|
+
|
30
30
|
PickForDivisionRarityAndQuality.call!(**context.to_h, allocations: allocations[tier],
|
31
|
-
|
31
|
+
picker: pickers[tier]).picks
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
@@ -1,16 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'active_support/core_ext/module/delegation'
|
4
|
-
require 'interactor'
|
5
4
|
|
6
5
|
module Sorare
|
7
6
|
module Rewards
|
8
7
|
module Cards
|
9
8
|
# PickForDivisionRarityAndQuality picks the rewards for a given division, rarity, and quality
|
10
|
-
# Receive a
|
11
|
-
#
|
12
|
-
# A game week league rarity supply
|
13
|
-
# { 'kylian-mbappe-lottin' => { 'rank' => 1 }, ...}
|
9
|
+
# Receive a picker
|
10
|
+
# A game week data
|
14
11
|
# And a reward allocations
|
15
12
|
# 15
|
16
13
|
class PickForDivisionRarityAndQuality
|
@@ -18,7 +15,8 @@ module Sorare
|
|
18
15
|
|
19
16
|
MISSING_CARDS = 'There are not enough cards to fulfill the requirements'
|
20
17
|
|
21
|
-
delegate :
|
18
|
+
delegate :picker, :allocations, :league, :rarity, :force, :picks, to: :context
|
19
|
+
delegate :game_week, to: :league
|
22
20
|
|
23
21
|
def call
|
24
22
|
context.picks = []
|
@@ -29,13 +27,20 @@ module Sorare
|
|
29
27
|
end
|
30
28
|
|
31
29
|
def pick!
|
32
|
-
while picks.length != allocations &&
|
33
|
-
|
34
|
-
|
30
|
+
pick_if_available! while picks.length != allocations && picker.length.positive?
|
31
|
+
end
|
32
|
+
|
33
|
+
def pick_if_available!
|
34
|
+
player = picker.pick
|
35
|
+
return if picker.player_counter[player] > game_week.player(player).supply(league, rarity).ceil
|
36
|
+
|
37
|
+
picks.push(player)
|
35
38
|
end
|
36
39
|
|
37
40
|
def reorder!
|
38
|
-
picks.sort!
|
41
|
+
picks.sort! do |a, b|
|
42
|
+
game_week.player(a).rank(league, rarity) <=> game_week.player(b).rank(league, rarity)
|
43
|
+
end
|
39
44
|
end
|
40
45
|
|
41
46
|
def check_length!
|
@@ -2,15 +2,12 @@
|
|
2
2
|
|
3
3
|
require 'active_support/core_ext/module/delegation'
|
4
4
|
require 'active_support/core_ext/enumerable'
|
5
|
-
require 'interactor'
|
6
|
-
require 'yaml'
|
7
5
|
|
8
6
|
module Sorare
|
9
7
|
module Rewards
|
10
8
|
module Cards
|
11
9
|
# PickForGameWeek picks the rewards for a given game week
|
12
|
-
# Receive a Game Week
|
13
|
-
# { public_seed: 123, playing_players: [...], supply: {...} }
|
10
|
+
# Receive a Game Week object
|
14
11
|
# And a reward allocations
|
15
12
|
# {
|
16
13
|
# 'global-all_star' => {
|
@@ -22,16 +19,17 @@ module Sorare
|
|
22
19
|
class PickForGameWeek
|
23
20
|
include Interactor
|
24
21
|
|
25
|
-
delegate :
|
22
|
+
delegate :game_week, :allocations, :salt, :picks, to: :context
|
26
23
|
|
27
24
|
def call
|
28
|
-
context.randomizer = Sorare::Rewards::Random.new(public_seed, salt)
|
29
25
|
context.picks = picks
|
30
26
|
end
|
31
27
|
|
32
28
|
def picks
|
33
|
-
allocations.
|
34
|
-
PickForLeague.call!(
|
29
|
+
allocations.each_league_allocations.each_with_object({}) do |(league_name, league_allocations), picks|
|
30
|
+
picks[league_name] = PickForLeague.call!(
|
31
|
+
**context.to_h, league: game_week.league(league_name), allocations: league_allocations
|
32
|
+
).league_picks
|
35
33
|
end
|
36
34
|
end
|
37
35
|
end
|
@@ -2,19 +2,13 @@
|
|
2
2
|
|
3
3
|
require 'active_support/core_ext/module/delegation'
|
4
4
|
require 'active_support/core_ext/enumerable'
|
5
|
-
require 'interactor'
|
6
|
-
require 'yaml'
|
7
5
|
|
8
6
|
module Sorare
|
9
7
|
module Rewards
|
10
8
|
module Cards
|
11
9
|
# PickForLeague picks the rewards for a given league
|
12
|
-
# Receive a game week
|
13
|
-
#
|
14
|
-
# 'rare' => { 'kylian-mbappe-lottin' => { 'rank' => 1 }, ...},
|
15
|
-
# 'super_rare' => { 'kylian-mbappe-lottin' => { 'rank' => 1 }, ...}
|
16
|
-
# }
|
17
|
-
# And a reward allocations
|
10
|
+
# Receive a game week object
|
11
|
+
# And a league allocations
|
18
12
|
# {
|
19
13
|
# 'D1' => {
|
20
14
|
# 'rare' => { 'tier_0' => 1, 'tier_1' => 2 }
|
@@ -23,40 +17,46 @@ module Sorare
|
|
23
17
|
class PickForLeague
|
24
18
|
include Interactor
|
25
19
|
|
26
|
-
delegate :
|
20
|
+
delegate :salt, :league, :allocations, :pickers, to: :context
|
21
|
+
delegate :game_week, to: :league
|
27
22
|
|
28
23
|
def call
|
29
|
-
context.
|
30
|
-
|
24
|
+
context.pickers = {}
|
25
|
+
add_pickers!
|
31
26
|
|
32
27
|
context.league_picks = allocations.keys.index_with do |division|
|
33
28
|
PickForDivision.call!(
|
34
|
-
**context.to_h,
|
29
|
+
**context.to_h, pickers: pickers, allocations: allocations[division]
|
35
30
|
).division_picks
|
36
31
|
end
|
37
32
|
end
|
38
33
|
|
39
|
-
def
|
40
|
-
supply.keys.index_with do |rarity|
|
41
|
-
|
34
|
+
def add_pickers!
|
35
|
+
league.supply.keys.index_with do |rarity|
|
36
|
+
pickers[rarity] = {}
|
42
37
|
qualify_and_add!(rarity)
|
43
38
|
end
|
44
39
|
end
|
45
40
|
|
46
41
|
def qualify_and_add!(rarity)
|
47
|
-
|
42
|
+
sorted_supply = league.supply[rarity]
|
43
|
+
qualified_players = Tiers::QualifyPlayers.call!(sorted_supply: sorted_supply).players
|
48
44
|
qualified_players.each_with_index.map do |players, tier|
|
49
45
|
transformed_tier = Sorare::Rewards.configuration.transform_tier.call(tier)
|
50
|
-
|
46
|
+
initialize_tier_picker(players, rarity, transformed_tier)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def initialize_tier_picker(players, rarity, tier)
|
51
|
+
pickers[rarity][tier] = Picker.new(game_week.public_seed, salt)
|
51
52
|
|
52
|
-
|
53
|
-
|
54
|
-
end
|
53
|
+
players.each do |slug|
|
54
|
+
pickers[rarity][tier].add_player(slug, player_supply(league, rarity, slug))
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
-
def
|
59
|
-
|
58
|
+
def player_supply(league, rarity, slug)
|
59
|
+
game_week.player(slug).overall_supply(league, rarity)
|
60
60
|
end
|
61
61
|
end
|
62
62
|
end
|
@@ -3,7 +3,6 @@
|
|
3
3
|
require 'active_support/core_ext/enumerable'
|
4
4
|
require 'active_support/core_ext/hash/indifferent_access'
|
5
5
|
require 'active_support/core_ext/module/delegation'
|
6
|
-
require 'interactor'
|
7
6
|
|
8
7
|
module Sorare
|
9
8
|
module Rewards
|
@@ -20,15 +19,21 @@ module Sorare
|
|
20
19
|
class Pick
|
21
20
|
include Interactor
|
22
21
|
|
23
|
-
delegate :
|
22
|
+
delegate :card_allocations, :game_week, :salt, to: :context
|
24
23
|
|
25
24
|
# Depending on the way the allocations interpreter is implemented it could be done by him or we
|
26
25
|
# update the allocations process to match the structure
|
27
26
|
def call
|
28
27
|
context.picks = Sorare::Rewards::Cards::PickForGameWeek.call!(
|
29
|
-
**
|
28
|
+
**context.to_h,
|
29
|
+
salt: salt,
|
30
|
+
allocations: transposed_allocations
|
30
31
|
).picks
|
31
32
|
end
|
33
|
+
|
34
|
+
def transposed_allocations
|
35
|
+
Sorare::Rewards::GameWeekAllocations.new(Transposer.transpose_allocations(card_allocations))
|
36
|
+
end
|
32
37
|
end
|
33
38
|
end
|
34
39
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/core_ext/module/delegation'
|
4
|
+
|
5
|
+
module Sorare
|
6
|
+
module Rewards
|
7
|
+
module PrizePools
|
8
|
+
# ComputeForDivision computes the prize pools of a division
|
9
|
+
class ComputeForDivision
|
10
|
+
include Interactor
|
11
|
+
|
12
|
+
delegate :playing_rate, :available_prize_pools, to: :context
|
13
|
+
|
14
|
+
def call
|
15
|
+
context.division_prize_pools = available_prize_pools.map do |available_prize_pool|
|
16
|
+
available_prize_pool * percentage_rewarded
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def percentage_rewarded
|
21
|
+
@percentage_rewarded ||= (config.find do |rate_config|
|
22
|
+
(rate_config.dig('playing_rates', 0)...rate_config.dig('playing_rates', 1)).include?(playing_rate)
|
23
|
+
end || config.last)['percentage_rewarded']
|
24
|
+
end
|
25
|
+
|
26
|
+
def config
|
27
|
+
Sorare::Rewards.configuration.prize_pool_configuration
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/core_ext/enumerable'
|
4
|
+
require 'active_support/core_ext/module/delegation'
|
5
|
+
|
6
|
+
module Sorare
|
7
|
+
module Rewards
|
8
|
+
module PrizePools
|
9
|
+
# ComputeForGameWeek computes the prize pools of a game week
|
10
|
+
class ComputeForGameWeek
|
11
|
+
include Interactor
|
12
|
+
|
13
|
+
delegate :game_week, to: :context
|
14
|
+
|
15
|
+
def call
|
16
|
+
context.prize_pools = prize_pools!
|
17
|
+
end
|
18
|
+
|
19
|
+
def prize_pools!
|
20
|
+
game_week.each_league.each_with_object(Hash.new({})) do |league, game_week_prize_pools|
|
21
|
+
game_week_prize_pools[league.name] = ComputeForLeague.call!(league: league).league_prize_pools
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/core_ext/module/delegation'
|
4
|
+
|
5
|
+
module Sorare
|
6
|
+
module Rewards
|
7
|
+
module PrizePools
|
8
|
+
# ComputeForLeague computes the prize pools of a league
|
9
|
+
class ComputeForLeague
|
10
|
+
include Interactor
|
11
|
+
|
12
|
+
delegate :league, to: :context
|
13
|
+
|
14
|
+
def call
|
15
|
+
context.league_prize_pools = prize_pools!
|
16
|
+
end
|
17
|
+
|
18
|
+
def prize_pools!
|
19
|
+
league.each_division_prize_pools.map do |division, prize_pools|
|
20
|
+
[
|
21
|
+
Sorare::Rewards.configuration.transform_division.call(division),
|
22
|
+
ComputeForDivision.call!(playing_rate: league.playing_rate, available_prize_pools: prize_pools)
|
23
|
+
.division_prize_pools
|
24
|
+
]
|
25
|
+
end.to_h
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
require 'active_support/core_ext/enumerable'
|
4
4
|
require 'active_support/core_ext/module/delegation'
|
5
|
-
require 'interactor'
|
6
5
|
|
7
6
|
module Sorare
|
8
7
|
module Rewards
|
@@ -11,16 +10,28 @@ module Sorare
|
|
11
10
|
class ComputeForGameWeek
|
12
11
|
include Interactor
|
13
12
|
|
14
|
-
delegate :
|
13
|
+
delegate :salt, :game_week, :game_week_supply, to: :context
|
15
14
|
|
16
15
|
def call
|
17
|
-
context.game_week_supply =
|
18
|
-
|
16
|
+
context.game_week_supply = game_week_supply!
|
17
|
+
end
|
18
|
+
|
19
|
+
def game_week_supply!
|
20
|
+
game_week.each_league.each_with_object(Hash.new({})) do |league, game_week_supply|
|
21
|
+
game_week_supply[league.name] = league_supply!(league)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def league_supply!(league)
|
26
|
+
league.each_rarity_supply.each_with_object({}) do |(rarity, supply), league_supply|
|
27
|
+
league_supply[rarity] = ComputeForRarity.call!(
|
28
|
+
league: league, rarity: rarity, supply: supply, randomizer: randomizer
|
29
|
+
).rarity_supply
|
19
30
|
end
|
20
31
|
end
|
21
32
|
|
22
33
|
def randomizer
|
23
|
-
@randomizer ||= Sorare::Rewards::Random.new(public_seed, salt)
|
34
|
+
@randomizer ||= Sorare::Rewards::Random.new(game_week.public_seed, salt)
|
24
35
|
end
|
25
36
|
end
|
26
37
|
end
|
@@ -1,8 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'active_support/core_ext/enumerable'
|
4
3
|
require 'active_support/core_ext/module/delegation'
|
5
|
-
require 'interactor'
|
6
4
|
|
7
5
|
module Sorare
|
8
6
|
module Rewards
|
@@ -11,8 +9,9 @@ module Sorare
|
|
11
9
|
class ComputeForRarity
|
12
10
|
include Interactor
|
13
11
|
|
14
|
-
delegate :randomizer, :
|
12
|
+
delegate :randomizer, :league, :rarity, :supply, :rewardable, :rarity_supply,
|
15
13
|
:minimum_remaining_games, to: :context
|
14
|
+
delegate :game_week, to: :league
|
16
15
|
|
17
16
|
def call
|
18
17
|
context.rarity_supply = []
|
@@ -22,7 +21,7 @@ module Sorare
|
|
22
21
|
end
|
23
22
|
|
24
23
|
def compute_rewardable_supply!
|
25
|
-
total_supply =
|
24
|
+
total_supply = supply.keys.sum do |player|
|
26
25
|
player_supply(player)
|
27
26
|
end
|
28
27
|
|
@@ -45,22 +44,13 @@ module Sorare
|
|
45
44
|
end
|
46
45
|
|
47
46
|
def player_supply(slug)
|
48
|
-
|
49
|
-
return 0 unless remaining_games.positive?
|
50
|
-
return 0 unless supply[slug]
|
51
|
-
|
52
|
-
supply[slug]['supply'].to_f / corrected_remaining_games(remaining_games)
|
47
|
+
game_week.player(slug).supply(league, rarity)
|
53
48
|
end
|
54
49
|
|
55
50
|
def rounded_supply(float_supply)
|
56
51
|
float_supply.floor + remaining_supply(float_supply.modulo(1))
|
57
52
|
end
|
58
53
|
|
59
|
-
# To be improved to handle properly seasons overlap
|
60
|
-
def corrected_remaining_games(remaining_games)
|
61
|
-
[remaining_games, minimum_remaining_games || 10].max
|
62
|
-
end
|
63
|
-
|
64
54
|
def remaining_supply(reward_probability)
|
65
55
|
probability = randomizer.rand
|
66
56
|
return 0 unless probability < reward_probability
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'active_support/core_ext/module/delegation'
|
4
|
-
require 'interactor'
|
5
4
|
|
6
5
|
module Sorare
|
7
6
|
module Rewards
|
@@ -19,9 +18,9 @@ module Sorare
|
|
19
18
|
|
20
19
|
# Use the tier specified within the data
|
21
20
|
def qualified_players_by_tier
|
22
|
-
tiers = Sorare::Rewards.configuration.tiers.times.map { |_|
|
21
|
+
tiers = Sorare::Rewards.configuration.tiers.times.map { |_| {} }
|
23
22
|
sorted_supply.each do |slug, data|
|
24
|
-
tiers[data['tier']]
|
23
|
+
tiers[data['tier']][slug] = data
|
25
24
|
end
|
26
25
|
|
27
26
|
tiers
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
module Sorare
|
6
|
+
module Rewards
|
7
|
+
# League stores the reward config for a league
|
8
|
+
class League
|
9
|
+
attr_reader :name, :supply, :config, :game_week
|
10
|
+
|
11
|
+
delegate :salt, to: :game_week
|
12
|
+
|
13
|
+
def initialize(name:, config:, supply:, game_week:)
|
14
|
+
@name = name
|
15
|
+
@config = config
|
16
|
+
@supply = supply
|
17
|
+
@game_week = game_week
|
18
|
+
end
|
19
|
+
|
20
|
+
%w[playing_rate prize_pools].each do |key|
|
21
|
+
define_method(key) do
|
22
|
+
config[key]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def each_division_prize_pools
|
27
|
+
prize_pools.each_with_index do |prize_pool, index|
|
28
|
+
return to_enum(:each_division_prize_pools) unless block_given?
|
29
|
+
|
30
|
+
yield(index + 1, prize_pool)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def each_rarity_supply
|
35
|
+
supply.each_key do |rarity|
|
36
|
+
return to_enum(:each_rarity_supply) unless block_given?
|
37
|
+
|
38
|
+
yield(rarity, supply[rarity])
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def each_rarity_tier_supply
|
43
|
+
each_rarity_supply do |rarity, rarity_supply|
|
44
|
+
qualified_supply = Tiers::QualifyPlayers.call!(sorted_supply: rarity_supply).players
|
45
|
+
qualified_supply.each_key.map do |tier|
|
46
|
+
return to_enum(:each_rarity_tier_supply) unless block_given?
|
47
|
+
|
48
|
+
transformed_tier = Sorare::Rewards.configuration.transform_tier.call(tier)
|
49
|
+
yield(rarity, transformed_tier, qualified_supply[tier])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'net/http'
|
5
|
+
|
6
|
+
module Sorare
|
7
|
+
module Rewards
|
8
|
+
# Picker allows to randomly pick a card
|
9
|
+
class Picker
|
10
|
+
attr_accessor :cards, :player_counter
|
11
|
+
|
12
|
+
delegate :length, to: :cards
|
13
|
+
|
14
|
+
def initialize(seed, salt)
|
15
|
+
@random = Random.new(seed, salt)
|
16
|
+
@cards = []
|
17
|
+
@player_counter = Hash.new(0)
|
18
|
+
@shuffled = false
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_player(slug, count)
|
22
|
+
raise "Can't add a player to a started draw" if @shuffled
|
23
|
+
|
24
|
+
@cards += Array.new(count, slug)
|
25
|
+
end
|
26
|
+
|
27
|
+
def pick
|
28
|
+
shuffle! unless @shuffled
|
29
|
+
|
30
|
+
@cards.shift.tap do |card|
|
31
|
+
@player_counter[card] += 1 if card
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def shuffle!
|
36
|
+
@cards.shuffle!(random: @random)
|
37
|
+
@shuffled = true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/core_ext/object/blank'
|
4
|
+
require 'time'
|
5
|
+
|
6
|
+
module Sorare
|
7
|
+
module Rewards
|
8
|
+
# Player stores the reward data of a player for a game week
|
9
|
+
class Player
|
10
|
+
attr_reader :slug
|
11
|
+
|
12
|
+
def initialize(slug, reward_data)
|
13
|
+
@slug = slug
|
14
|
+
@supply = Hash.new { |h, k| h[k] = {} }
|
15
|
+
@config = reward_data.config
|
16
|
+
@game_data = reward_data.playing_players[slug]
|
17
|
+
load_supply(reward_data)
|
18
|
+
end
|
19
|
+
|
20
|
+
def rewardable?(league, rarity)
|
21
|
+
!@config.cooldown?(rarity, @game_data.dig(league, rarity) || [])
|
22
|
+
end
|
23
|
+
|
24
|
+
def playing?
|
25
|
+
@game_data.present?
|
26
|
+
end
|
27
|
+
|
28
|
+
def supply(league, rarity)
|
29
|
+
return 0 unless playing? && remaining_games_for_supply.positive?
|
30
|
+
|
31
|
+
[
|
32
|
+
uncapped_supply_for_game_week(league, rarity), # Remaining supply
|
33
|
+
available_during_cooldown(league, rarity), # Cooldown limit
|
34
|
+
@config.distribution_limit(rarity) # Hard limit
|
35
|
+
].min
|
36
|
+
end
|
37
|
+
|
38
|
+
def rank(league, rarity)
|
39
|
+
@supply.dig(league.name, rarity, 'rank')
|
40
|
+
end
|
41
|
+
|
42
|
+
def overall_supply(league, rarity)
|
43
|
+
@supply.dig(league.name, rarity, 'supply') || 0
|
44
|
+
end
|
45
|
+
|
46
|
+
def uncapped_supply_for_game_week(league, rarity)
|
47
|
+
overall_supply(league, rarity) / remaining_games_for_supply.to_f
|
48
|
+
end
|
49
|
+
|
50
|
+
def available_during_cooldown(league, rarity)
|
51
|
+
[@config.cooldown_limit(rarity) - rewarded_during_cooldown(league, rarity), 0].max
|
52
|
+
end
|
53
|
+
|
54
|
+
def rewarded_during_cooldown(league, rarity)
|
55
|
+
(rewards&.dig(league) || []).count { |at| (Time.parse(at) + @config.cooldown_since(rarity)) < Time.now }
|
56
|
+
end
|
57
|
+
|
58
|
+
%w[rewards remaining_games_for_supply remaining_games].each do |key|
|
59
|
+
define_method(key) do
|
60
|
+
@game_data&.dig(key)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def load_supply(reward_data)
|
67
|
+
reward_data.each_league do |league|
|
68
|
+
league.each_rarity_supply do |rarity, supply|
|
69
|
+
next unless supply[slug]
|
70
|
+
|
71
|
+
@supply[league.name][rarity] = supply[slug]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
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:
|
4
|
+
version: 1.0.0.beta5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- pierre
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-06-
|
11
|
+
date: 2021-06-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -168,9 +168,14 @@ files:
|
|
168
168
|
- Rakefile
|
169
169
|
- bin/console
|
170
170
|
- bin/setup
|
171
|
+
- data.patch
|
171
172
|
- lib/sorare/rewards.rb
|
172
173
|
- lib/sorare/rewards/allocation_configuration.yml
|
173
174
|
- lib/sorare/rewards/configuration.rb
|
175
|
+
- lib/sorare/rewards/game_week.rb
|
176
|
+
- lib/sorare/rewards/game_week_allocations.rb
|
177
|
+
- lib/sorare/rewards/game_week_config.rb
|
178
|
+
- lib/sorare/rewards/interactor.rb
|
174
179
|
- lib/sorare/rewards/interactors/allocations/compute_for_game_week.rb
|
175
180
|
- lib/sorare/rewards/interactors/allocations/compute_for_league.rb
|
176
181
|
- lib/sorare/rewards/interactors/allocations/compute_for_quality.rb
|
@@ -182,12 +187,18 @@ files:
|
|
182
187
|
- lib/sorare/rewards/interactors/cards/pick_for_game_week.rb
|
183
188
|
- lib/sorare/rewards/interactors/cards/pick_for_league.rb
|
184
189
|
- lib/sorare/rewards/interactors/pick.rb
|
190
|
+
- lib/sorare/rewards/interactors/prize_pools/compute_for_division.rb
|
191
|
+
- lib/sorare/rewards/interactors/prize_pools/compute_for_game_week.rb
|
192
|
+
- lib/sorare/rewards/interactors/prize_pools/compute_for_league.rb
|
185
193
|
- lib/sorare/rewards/interactors/supply/compute_for_game_week.rb
|
186
|
-
- lib/sorare/rewards/interactors/supply/compute_for_league.rb
|
187
194
|
- lib/sorare/rewards/interactors/supply/compute_for_quality.rb
|
188
195
|
- lib/sorare/rewards/interactors/supply/compute_for_rarity.rb
|
189
196
|
- lib/sorare/rewards/interactors/tiers/qualify_players.rb
|
190
197
|
- lib/sorare/rewards/interactors/tiers/qualify_supply.rb
|
198
|
+
- lib/sorare/rewards/league.rb
|
199
|
+
- lib/sorare/rewards/picker.rb
|
200
|
+
- lib/sorare/rewards/player.rb
|
201
|
+
- lib/sorare/rewards/prize_pool_configuration.yml
|
191
202
|
- lib/sorare/rewards/random.rb
|
192
203
|
- lib/sorare/rewards/transposer.rb
|
193
204
|
- lib/sorare/rewards/version.rb
|
@@ -1,24 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'active_support/core_ext/enumerable'
|
4
|
-
require 'active_support/core_ext/module/delegation'
|
5
|
-
require 'interactor'
|
6
|
-
|
7
|
-
module Sorare
|
8
|
-
module Rewards
|
9
|
-
module Supply
|
10
|
-
# ComputeForLeague computes the rewardable supply of a league
|
11
|
-
class ComputeForLeague
|
12
|
-
include Interactor
|
13
|
-
|
14
|
-
delegate :randomizer, :supply, :playing_players, to: :context
|
15
|
-
|
16
|
-
def call
|
17
|
-
context.league_supply = supply.keys.index_with do |rarity|
|
18
|
-
ComputeForRarity.call!(**context.to_h, supply: supply[rarity]).rarity_supply
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|