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.
@@ -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
- class SimulatorTimeline < ActiveRecord::Base
2
- belongs_to :match
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, :home_players, :visitor_goals, :visitor_cards, :visitor_players
7
+ attr_accessor :narration, :home_goals, :home_cards, :current_home_players, :visitor_goals, :visitor_cards, :current_visitor_players
6
8
 
7
- def period(period)
8
- if block_given?
9
- yield Period.new(self, period)
10
- else
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 current_period_time
16
- SimulatorMatch::STATUS_SEQUENCE[current_period] || current_period.to_i
15
+ def timeline_rand(limit = nil)
16
+ @random.rand limit
17
17
  end
18
18
 
19
- def transitions
20
- states = States.new self
21
- yield(states)
22
- states.set_current
19
+ def sample(collection)
20
+ collection[timeline_rand(collection.size)]
23
21
  end
24
22
 
25
- def setup
26
- @narration = []
27
- @home_goals = []
28
- @home_cards = []
29
- @home_players = []
30
- @visitor_goals = []
31
- @visitor_cards = []
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
- class States
37
- def initialize(timeline)
38
- @timeline = timeline
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
- def set_current
43
- delta = (Time.now - @timeline.match.scheduled_start).to_i
44
- period = @timeline.states.find{|minute, status| delta > minute }[1]
36
+ def visitor_score
37
+ visitor_goals.count{ |g| g.type == 'Favor' } + home_goals.count{ |g| g.type != 'Favor' }
38
+ end
45
39
 
46
- @timeline.current_period = period
40
+ def home_team
41
+ @match.home_team
42
+ end
47
43
 
48
- @timeline.current_minute if period.to_s.match(/(period|prorrogation)/)
49
- started_second = @timeline.states.find{ |minute, status| status == period }[0]
50
- ((Time.now - @timeline.match.scheduled_start) + started_second).from_now
51
- else
52
- 0
53
- end
54
- end
44
+ def visitor_team
45
+ @match.visitor_team
46
+ end
55
47
 
56
- def add_transition(status, time)
57
- @timeline.states << [time, status]
58
- @timeline.sort!
59
- end
48
+ def home_players
49
+ @home_players ||= @match.home_simulator_team.simulator_players.order(:source_id)
50
+ end
60
51
 
61
- def method_missing(method_name, *args, &block)
62
- if SimulatorMatch::STATUS_SEQUENCE[method_name]
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
- class Period
71
- def initialize(timeline, period)
72
- @timeline, @period = timeline, period
73
- end
56
+ def all_current_players
57
+ current_home_players + current_visitor_players
58
+ end
74
59
 
75
- def period_time
76
- SimulatorMatch::STATUS_SEQUENCE[@period] || @period.to_i
77
- end
60
+ def all_players
61
+ home_players + visitor_players
62
+ end
78
63
 
79
- def at(minute)
80
- if block_given?
81
- yield Minute.new(@timeline, period_time, @minute)
82
- else
83
- Minute.new(@timeline, period_time, @minute)
84
- end
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
- class Minute
89
- def initialize(timeline, minute)
90
- @timeline, @minute = timeline, minute
91
- end
72
+ def transitions(&block)
73
+ States.new(self, &block)
74
+ end
92
75
 
93
- def event(type, *args)
94
- [@timeline, @minute, type, args]
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
- def add_home_goal(player_source_id, type = 'Favor', reference = nil)
98
- @timeline.home_goals << event(:goal, player_source_id, type, reference)
99
- end
84
+ def current_period_time
85
+ SimulatorMatch::STATUS_SEQUENCE[current_period] || current_period.to_i
86
+ end
100
87
 
101
- def add_visitor_goal(player_source_id, type = 'Favor', reference = nil)
102
- @timeline.visitor_goals << event(:goal, player_source_id, type, reference)
103
- end
88
+ protected
89
+ def load_timeline
90
+ @random = Random.new @match.timeline_random_seed
104
91
 
105
- def remove_goal(reference)
106
- @timeline.home_goals.delete_all do |period, minute, type, *args|
107
- type == goal && args[2] == reference
108
- end
109
- end
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