sorare-rewards 0.1.0.beta11 → 1.0.0.beta4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/data.patch +0 -0
- 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
- data/lib/sorare/rewards.rb +0 -9
- 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: 8b703ed076cf77397da8c97fcae63c48379b11fda722bcb047c9e9a7e2f47fb9
|
4
|
+
data.tar.gz: 6a428a838d574916a4a5ad3dedbc79046845a4f9b6009b28fde70adbc1ac673d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cea7dd5e7e61647fa6a30718917059a1dd25f34d864c6214f44959d4a546665db3b0125a00742da5216d1ed56284d28f4f5403cca77fb1c714089759aaf371de
|
7
|
+
data.tar.gz: dd66f18067ae9b07202ac98d0e89b2a80cc52042726ab40eb6da73fa947905aca45343b319815ed8917031ba53dd993ca90b6ec2a484f9e05dea75449e9954e4
|
data/Gemfile.lock
CHANGED
data/data.patch
ADDED
File without changes
|
@@ -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]
|
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
|
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
|
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.beta4
|
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
|