foot_stats_simulator 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|