foot_stats_simulator 0.0.1 → 0.0.2
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.
- data/app/controllers/foot_stats_simulator/api_controller.rb +18 -13
- data/app/models/simulator_championship.rb +9 -1
- data/app/models/simulator_championship_participation.rb +122 -0
- data/app/models/simulator_classification.rb +119 -0
- data/app/models/simulator_match.rb +231 -26
- data/app/models/simulator_player.rb +26 -1
- data/app/models/simulator_team.rb +18 -0
- data/app/models/simulator_timeline.rb +74 -82
- data/app/models/simulator_timeline/card_event.rb +41 -0
- data/app/models/simulator_timeline/event.rb +26 -0
- data/app/models/simulator_timeline/event_handler.rb +69 -0
- data/app/models/simulator_timeline/goal_event.rb +40 -0
- data/app/models/simulator_timeline/lorem.rb +23 -0
- data/app/models/simulator_timeline/lorem_messages.rb +185 -0
- data/app/models/simulator_timeline/minute.rb +26 -0
- data/app/models/simulator_timeline/narration_event.rb +39 -0
- data/app/models/simulator_timeline/period.rb +29 -0
- data/app/models/simulator_timeline/player_event.rb +65 -0
- data/app/models/simulator_timeline/states.rb +45 -0
- data/config/routes.rb +2 -4
- data/lib/foot_stats_simulator.rb +13 -1
- data/lib/foot_stats_simulator/migrate.rb +44 -34
- data/lib/foot_stats_simulator/tasks/foot_stats_simulator.rake +40 -21
- data/lib/foot_stats_simulator/version.rb +1 -1
- metadata +31 -2
@@ -1,9 +1,34 @@
|
|
1
1
|
class SimulatorPlayer < ActiveRecord::Base
|
2
|
+
belongs_to :simulator_team
|
3
|
+
|
4
|
+
def to_foot_stats
|
5
|
+
{
|
6
|
+
'@Id' => self.source_id,
|
7
|
+
'@Nome' => self.full_name,
|
8
|
+
'@Apelido' => self.nickname
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.to_foot_stats
|
13
|
+
# FootStats suck!
|
14
|
+
# Yes, it's stupid, but it's true...
|
15
|
+
# FootStats doesn't return a collection on this...
|
16
|
+
if count == 0
|
17
|
+
nil
|
18
|
+
else
|
19
|
+
if count == 1
|
20
|
+
self.first.to_foot_stats
|
21
|
+
else
|
22
|
+
{ 'Equipe' => all.map(&:to_foot_stats) }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
2
27
|
def self.dump!
|
3
28
|
SimulatorTeam.all.each do |team|
|
4
29
|
FootStats::Player.all(team: team.source_id).each do |foot_stats_player|
|
5
30
|
player = self.find_or_create_by_source_id foot_stats_player.source_id
|
6
|
-
player.update_attributes foot_stats_player.attributes
|
31
|
+
player.update_attributes foot_stats_player.attributes.merge(simulator_team_id: team.id)
|
7
32
|
end
|
8
33
|
end
|
9
34
|
end
|
@@ -1,9 +1,27 @@
|
|
1
1
|
class SimulatorTeam < ActiveRecord::Base
|
2
|
+
has_many :simulator_championship_participations
|
3
|
+
has_many :simulator_championships, through: :simulator_championship_participations
|
4
|
+
has_many :simulator_players
|
5
|
+
|
6
|
+
def to_foot_stats
|
7
|
+
{
|
8
|
+
'@Id' => self.source_id,
|
9
|
+
'@Nome' => self.full_name,
|
10
|
+
'@Cidade' => self.city,
|
11
|
+
'@Pais' => self.country
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.to_foot_stats
|
16
|
+
{ 'Equipe' => all.map(&:to_foot_stats) }
|
17
|
+
end
|
18
|
+
|
2
19
|
def self.dump!
|
3
20
|
SimulatorChampionship.all.each do |championship|
|
4
21
|
FootStats::Team.all(championship: championship.source_id).each do |foot_stats_team|
|
5
22
|
team = self.find_or_create_by_source_id foot_stats_team.source_id
|
6
23
|
team.update_attributes foot_stats_team.attributes
|
24
|
+
SimulatorChampionshipParticipation.find_or_create_by_simulator_team_id_and_simulator_championship_id team.id, championship.id
|
7
25
|
end
|
8
26
|
end
|
9
27
|
end
|
@@ -1,111 +1,103 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# TODO: Handle penalties
|
2
|
+
|
3
|
+
class SimulatorTimeline
|
4
|
+
attr_reader :match
|
3
5
|
|
4
6
|
attr_accessor :states, :current_period, :current_minute
|
5
|
-
attr_accessor :narration, :home_goals, :home_cards, :
|
7
|
+
attr_accessor :narration, :home_goals, :home_cards, :current_home_players, :visitor_goals, :visitor_cards, :current_visitor_players
|
6
8
|
|
7
|
-
def
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
Period.new(self, period_time)
|
12
|
-
end
|
9
|
+
def initialize(match, timeline_name)
|
10
|
+
@match = match
|
11
|
+
@timeline_path = File.expand_path File.join(FootStatsSimulator.timelines_dir, "#{timeline_name}.rb")
|
12
|
+
load_timeline
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
16
|
-
|
15
|
+
def timeline_rand(limit = nil)
|
16
|
+
@random.rand limit
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
20
|
-
|
21
|
-
yield(states)
|
22
|
-
states.set_current
|
19
|
+
def sample(collection)
|
20
|
+
collection[timeline_rand(collection.size)]
|
23
21
|
end
|
24
22
|
|
25
|
-
def
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
@visitor_players = []
|
33
|
-
yield(self)
|
23
|
+
def collection_of(n, collection)
|
24
|
+
collecteds = []
|
25
|
+
while collecteds.size < n
|
26
|
+
collected = sample(collection)
|
27
|
+
collecteds << collected unless collecteds.include?(collected)
|
28
|
+
end
|
29
|
+
collecteds
|
34
30
|
end
|
35
31
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
@timeline.states = []
|
40
|
-
end
|
32
|
+
def home_score
|
33
|
+
home_goals.count{ |g| g.type == 'Favor' } + visitor_goals.count{ |g| g.type != 'Favor' }
|
34
|
+
end
|
41
35
|
|
42
|
-
|
43
|
-
|
44
|
-
|
36
|
+
def visitor_score
|
37
|
+
visitor_goals.count{ |g| g.type == 'Favor' } + home_goals.count{ |g| g.type != 'Favor' }
|
38
|
+
end
|
45
39
|
|
46
|
-
|
40
|
+
def home_team
|
41
|
+
@match.home_team
|
42
|
+
end
|
47
43
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
else
|
52
|
-
0
|
53
|
-
end
|
54
|
-
end
|
44
|
+
def visitor_team
|
45
|
+
@match.visitor_team
|
46
|
+
end
|
55
47
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
end
|
48
|
+
def home_players
|
49
|
+
@home_players ||= @match.home_simulator_team.simulator_players.order(:source_id)
|
50
|
+
end
|
60
51
|
|
61
|
-
|
62
|
-
|
63
|
-
add_transition(method_name, args.first)
|
64
|
-
else
|
65
|
-
super(method_name, *args, &block)
|
66
|
-
end
|
67
|
-
end
|
52
|
+
def visitor_players
|
53
|
+
@visitor_players ||= @match.visitor_simulator_team.simulator_players.order(:source_id)
|
68
54
|
end
|
69
55
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
end
|
56
|
+
def all_current_players
|
57
|
+
current_home_players + current_visitor_players
|
58
|
+
end
|
74
59
|
|
75
|
-
|
76
|
-
|
77
|
-
|
60
|
+
def all_players
|
61
|
+
home_players + visitor_players
|
62
|
+
end
|
78
63
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
end
|
64
|
+
def update_match
|
65
|
+
@match.update_attributes status: current_period,
|
66
|
+
home_score: home_score,
|
67
|
+
home_penalties_score: nil,
|
68
|
+
visitor_score: visitor_score,
|
69
|
+
visitor_penalties_score: nil
|
86
70
|
end
|
87
71
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
end
|
72
|
+
def transitions(&block)
|
73
|
+
States.new(self, &block)
|
74
|
+
end
|
92
75
|
|
93
|
-
|
94
|
-
|
76
|
+
def period(period)
|
77
|
+
if block_given?
|
78
|
+
yield Period.new(self, period)
|
79
|
+
else
|
80
|
+
Period.new(self, period_time)
|
95
81
|
end
|
82
|
+
end
|
96
83
|
|
97
|
-
|
98
|
-
|
99
|
-
|
84
|
+
def current_period_time
|
85
|
+
SimulatorMatch::STATUS_SEQUENCE[current_period] || current_period.to_i
|
86
|
+
end
|
100
87
|
|
101
|
-
|
102
|
-
|
103
|
-
|
88
|
+
protected
|
89
|
+
def load_timeline
|
90
|
+
@random = Random.new @match.timeline_random_seed
|
104
91
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
92
|
+
@states = []
|
93
|
+
@narration = []
|
94
|
+
@home_goals = []
|
95
|
+
@home_cards = []
|
96
|
+
@current_home_players = []
|
97
|
+
@visitor_goals = []
|
98
|
+
@visitor_cards = []
|
99
|
+
@current_visitor_players = []
|
100
|
+
|
101
|
+
eval File.read(@timeline_path)
|
110
102
|
end
|
111
103
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class SimulatorTimeline
|
2
|
+
class CardEvent < SimulatorTimeline::Event
|
3
|
+
attr_reader :player, :type, :reference
|
4
|
+
|
5
|
+
def initialize(event_handler, timeline, period_time, minute, options = {})
|
6
|
+
@player, @type, @reference = options.values_at :player, :type, :reference
|
7
|
+
@type ||= :yellow
|
8
|
+
super(event_handler, timeline, period_time, minute, options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_foot_stats
|
12
|
+
{
|
13
|
+
"@Jogador" => @player.full_name,
|
14
|
+
"@Periodo" => SimulatorMatch::STATUS_MAP[SimulatorMatch::STATUSES[@period_time]],
|
15
|
+
"@Minuto" => @minute.to_s,
|
16
|
+
"@Tipo" => @type
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.remove(timeline, reference)
|
21
|
+
timeline.home_cards.delete_if{ |event| event.reference == reference }
|
22
|
+
timeline.visitor_cards.delete_if{ |event| event.reference == reference }
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.to_foot_stats(array_of_cards)
|
26
|
+
fuck_collection 'Cartoes', array_of_cards.map(&:to_foot_stats)
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
def apply_event
|
31
|
+
if @player.simulator_team == @timeline.home_team
|
32
|
+
@timeline.home_cards << self
|
33
|
+
else
|
34
|
+
@timeline.visitor_cards << self
|
35
|
+
end
|
36
|
+
|
37
|
+
card_kind = (@type == :yellow) ? 'amarelo' : 'vermelho'
|
38
|
+
event_handler.narration :card, current_player: @player.nickname, card_kind: card_kind
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class SimulatorTimeline
|
2
|
+
class Event
|
3
|
+
attr_reader :period_time, :minute, :event_handler
|
4
|
+
|
5
|
+
def initialize(event_handler, timeline, period_time, minute, options = {})
|
6
|
+
@event_handler, @timeline, @period_time, @minute = event_handler, timeline, period_time, minute
|
7
|
+
apply_event
|
8
|
+
end
|
9
|
+
|
10
|
+
def <=>(other)
|
11
|
+
[@period_time, @minute] <=> [other.period_time, other.minute]
|
12
|
+
end
|
13
|
+
|
14
|
+
# Yeah... FootStats fucks collection...
|
15
|
+
def self.fuck_collection(key, collection)
|
16
|
+
case collection.size
|
17
|
+
when 0
|
18
|
+
nil
|
19
|
+
when 1
|
20
|
+
{ key => collection.first }
|
21
|
+
else
|
22
|
+
{ key => collection }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
class SimulatorTimeline
|
2
|
+
class EventHandler
|
3
|
+
def initialize(timeline, period_time, minute)
|
4
|
+
@timeline, @period_time, @minute = timeline, period_time, minute
|
5
|
+
end
|
6
|
+
|
7
|
+
def add_goal(player, options={})
|
8
|
+
options = { player: player }.merge(options)
|
9
|
+
GoalEvent.new self, @timeline, @period_time, @minute, options
|
10
|
+
end
|
11
|
+
|
12
|
+
def remove_goal(reference)
|
13
|
+
GoalEvent.remove @timeline, reference
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_card(player, options={})
|
17
|
+
options = { player: player }.merge(options)
|
18
|
+
CardEvent.new self, @timeline, @period_time, @minute, options
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_yellow_card(player, options={})
|
22
|
+
options = { player: player, type: :yellow }.merge(options)
|
23
|
+
add_card player, options
|
24
|
+
end
|
25
|
+
|
26
|
+
def add_red_card(player, options={})
|
27
|
+
options = { player: player, type: :red }.merge(options)
|
28
|
+
add_card player, options
|
29
|
+
end
|
30
|
+
|
31
|
+
def remove_card(reference)
|
32
|
+
CardEvent.remove @timeline, reference
|
33
|
+
end
|
34
|
+
|
35
|
+
def escalation(players)
|
36
|
+
players.each do |player|
|
37
|
+
self.player(player)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def player(player, options={})
|
42
|
+
options = { player: player }.merge(options)
|
43
|
+
PlayerEvent.new self, @timeline, @period_time, @minute, options
|
44
|
+
end
|
45
|
+
|
46
|
+
def remove_player(reference)
|
47
|
+
PlayerEvent.remove @timeline, reference
|
48
|
+
end
|
49
|
+
|
50
|
+
def narration(message_name = :lorem, options = {})
|
51
|
+
message = if message_name.is_a?(String)
|
52
|
+
message_name
|
53
|
+
else
|
54
|
+
message = Lorem.new(@timeline, @period_time, @minute, options).public_send message_name
|
55
|
+
end
|
56
|
+
options = { message: message }.merge(options)
|
57
|
+
NarrationEvent.new self, @timeline, @period_time, @minute, options
|
58
|
+
end
|
59
|
+
|
60
|
+
def remove_narration(reference)
|
61
|
+
NarrationEvent.remove @timeline, reference
|
62
|
+
end
|
63
|
+
|
64
|
+
protected
|
65
|
+
def event(type, *args)
|
66
|
+
[@period_time, @minute, type, args]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class SimulatorTimeline
|
2
|
+
class GoalEvent < SimulatorTimeline::Event
|
3
|
+
attr_reader :player, :type, :reference
|
4
|
+
|
5
|
+
def initialize(event_handler, timeline, period_time, minute, options = {})
|
6
|
+
@player, @type, @reference = options.values_at :player, :type, :reference
|
7
|
+
@type ||= 'Favor'
|
8
|
+
super(event_handler, timeline, period_time, minute, options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_foot_stats
|
12
|
+
{
|
13
|
+
"@Jogador" => @player.full_name,
|
14
|
+
"@Periodo" => SimulatorMatch::STATUS_MAP[SimulatorMatch::STATUSES[@period_time]],
|
15
|
+
"@Minuto" => @minute.to_s,
|
16
|
+
"@Tipo" => @type
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.remove(timeline, reference)
|
21
|
+
timeline.home_goals.delete_if{ |event| event.reference == reference }
|
22
|
+
timeline.visitor_goals.delete_if{ |event| event.reference == reference }
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.to_foot_stats(array_of_goals)
|
26
|
+
fuck_collection 'Gol', array_of_goals.map(&:to_foot_stats)
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
def apply_event
|
31
|
+
if @player.simulator_team == @timeline.home_team
|
32
|
+
@timeline.home_goals << self
|
33
|
+
else
|
34
|
+
@timeline.visitor_goals << self
|
35
|
+
end
|
36
|
+
|
37
|
+
event_handler.narration :goal, current_player: @player.nickname, player_team: @player.simulator_team.full_name
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class SimulatorTimeline
|
2
|
+
class Lorem
|
3
|
+
attr_reader :timeline, :period_time, :minute
|
4
|
+
include LoremMessages
|
5
|
+
|
6
|
+
def initialize(timeline, period_time, minute, options = {})
|
7
|
+
@timeline, @period_time, @minute, @options = timeline, period_time, minute, options
|
8
|
+
end
|
9
|
+
|
10
|
+
def method_missing(method_name, *args, &block)
|
11
|
+
return render(@options[method_name]) if @options[method_name]
|
12
|
+
return render(send("#{method_name}_message", *args, &block)) if respond_to?("#{method_name}_message")
|
13
|
+
return @timeline.public_send(method_name, *args, &block) if @timeline.respond_to?(method_name)
|
14
|
+
super(method_name, *args, &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
def render(sentence)
|
19
|
+
normalized_sentence = sentence.is_a?(String) ? sentence : sample(sentence)
|
20
|
+
eval Erubis::Eruby.new(normalized_sentence).src, binding
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|