rubygoal 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/bin/rubygoal +12 -2
  3. data/lib/rubygoal.rb +1 -18
  4. data/lib/rubygoal/ball.rb +60 -0
  5. data/lib/rubygoal/coach.rb +81 -0
  6. data/lib/rubygoal/coach_definition.rb +54 -0
  7. data/lib/rubygoal/coach_loader.rb +55 -0
  8. data/lib/rubygoal/coaches/coach_definition_away.rb +72 -0
  9. data/lib/rubygoal/coaches/coach_definition_home.rb +76 -0
  10. data/lib/rubygoal/configuration.rb +49 -0
  11. data/lib/rubygoal/coordinate.rb +33 -0
  12. data/lib/rubygoal/field.rb +134 -0
  13. data/lib/rubygoal/formation.rb +73 -0
  14. data/lib/rubygoal/formation/formation_dsl.rb +67 -0
  15. data/lib/rubygoal/game.rb +162 -0
  16. data/lib/rubygoal/goal.rb +26 -0
  17. data/lib/rubygoal/match_data.rb +130 -0
  18. data/lib/rubygoal/moveable.rb +68 -0
  19. data/lib/rubygoal/player.rb +87 -0
  20. data/lib/rubygoal/players/average.rb +16 -0
  21. data/lib/rubygoal/players/captain.rb +15 -0
  22. data/lib/rubygoal/players/fast.rb +16 -0
  23. data/lib/rubygoal/players/goalkeeper.rb +26 -0
  24. data/lib/rubygoal/players/player_movement.rb +98 -0
  25. data/lib/rubygoal/recorder.rb +53 -0
  26. data/lib/rubygoal/simulator.rb +33 -0
  27. data/lib/rubygoal/team.rb +198 -0
  28. data/lib/rubygoal/teams/away.rb +15 -0
  29. data/lib/rubygoal/teams/home.rb +15 -0
  30. data/lib/rubygoal/util.rb +36 -0
  31. data/lib/rubygoal/version.rb +3 -0
  32. metadata +35 -23
  33. data/lib/rubygoal/gui.rb +0 -20
  34. data/lib/rubygoal/gui/ball.rb +0 -26
  35. data/lib/rubygoal/gui/field.rb +0 -21
  36. data/lib/rubygoal/gui/game.rb +0 -166
  37. data/lib/rubygoal/gui/goal.rb +0 -23
  38. data/lib/rubygoal/gui/players.rb +0 -28
  39. data/lib/rubygoal/gui/version.rb +0 -5
  40. data/media/average_away.png +0 -0
  41. data/media/average_home.png +0 -0
  42. data/media/background.png +0 -0
  43. data/media/ball.png +0 -0
  44. data/media/captain_away.png +0 -0
  45. data/media/captain_home.png +0 -0
  46. data/media/fast_away.png +0 -0
  47. data/media/fast_home.png +0 -0
  48. data/media/goal.png +0 -0
@@ -0,0 +1,49 @@
1
+ require 'rubygoal/coordinate'
2
+
3
+ module Rubygoal
4
+ class Configuration
5
+ attr_accessor :average_players_count, :fast_players_count, :captain_players_count,
6
+ :kick_strength, :kick_again_delay, :distance_control_ball,
7
+ :deceleration_coef, :initial_player_positions, :game_time,
8
+ :average_lower_error, :average_upper_error, :average_speed,
9
+ :fast_lower_error, :fast_upper_error, :fast_speed,
10
+ :captain_error, :captain_speed, :debug_output, :record_game
11
+ end
12
+
13
+ class << self
14
+ attr_writer :configuration
15
+
16
+ def configuration
17
+ @configuration ||= Configuration.new
18
+ end
19
+
20
+ def configure
21
+ yield(configuration)
22
+ end
23
+ end
24
+
25
+ Rubygoal.configure do |config|
26
+ config.average_players_count = 6
27
+ config.fast_players_count = 3
28
+
29
+ config.kick_strength = 20
30
+ config.kick_again_delay = 1
31
+ config.distance_control_ball = 30
32
+ config.deceleration_coef = 0.95
33
+
34
+ config.average_lower_error = 0.1
35
+ config.average_upper_error = 0.15
36
+ config.average_speed = 3.5
37
+
38
+ config.fast_lower_error = 0.1
39
+ config.fast_upper_error = 0.15
40
+ config.fast_speed = 4
41
+
42
+ config.captain_error = 0.05
43
+ config.captain_speed = 4.5
44
+
45
+ config.game_time = 120
46
+ config.debug_output = true
47
+ config.record_game = false
48
+ end
49
+ end
@@ -0,0 +1,33 @@
1
+ require 'rubygoal/util'
2
+
3
+ module Rubygoal
4
+ class Coordinate < Struct.new(:x, :y)
5
+ def distance(coordinate)
6
+ Util.distance(x, y, coordinate.x, coordinate.y)
7
+ end
8
+
9
+ def add(coordinate)
10
+ self.class.new(x + coordinate.x, y + coordinate.y)
11
+ end
12
+
13
+ def mult(coeficient)
14
+ self.class.new(x * coeficient, y * coeficient)
15
+ end
16
+
17
+ def to_s
18
+ "(#{x}, #{y})"
19
+ end
20
+
21
+ def to_hash
22
+ { x: x, y: y }
23
+ end
24
+ end
25
+
26
+ class Position < Coordinate; end
27
+
28
+ class Velocity < Coordinate
29
+ def nonzero?
30
+ x != 0 || y != 0
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,134 @@
1
+ require 'forwardable'
2
+
3
+ require 'rubygoal/ball'
4
+ require 'rubygoal/coach_loader'
5
+ require 'rubygoal/teams/home'
6
+ require 'rubygoal/teams/away'
7
+ module Rubygoal
8
+ module Field
9
+ WIDTH = 1394.0
10
+ HEIGHT = 938.0
11
+ OFFSET = Position.new(262, 112)
12
+ GOAL_HEIGHT = 275
13
+ CLOSE_GOAL_DISTANCE = 275
14
+
15
+ class << self
16
+ def center_position
17
+ center = Position.new(
18
+ WIDTH / 2,
19
+ HEIGHT / 2
20
+ )
21
+ OFFSET.add(center)
22
+ end
23
+
24
+ def goal_position(side)
25
+ position = center_position
26
+ case side
27
+ when :home
28
+ position.x = OFFSET.x
29
+ when :away
30
+ position.x = OFFSET.x + WIDTH
31
+ end
32
+
33
+ position
34
+ end
35
+
36
+ def absolute_position(field_position, side)
37
+ case side
38
+ when :home
39
+ OFFSET.add(field_position)
40
+ when :away
41
+ OFFSET.add(
42
+ Position.new(
43
+ WIDTH - field_position.x,
44
+ HEIGHT - field_position.y
45
+ )
46
+ )
47
+ end
48
+ end
49
+
50
+ def field_position(absolute_position, side)
51
+ case side
52
+ when :home
53
+ absolute_position.add(
54
+ Position.new(
55
+ - OFFSET.x,
56
+ - OFFSET.y
57
+ )
58
+ )
59
+ when :away
60
+ Position.new(
61
+ WIDTH - (absolute_position.x - OFFSET.x),
62
+ HEIGHT - (absolute_position.y - OFFSET.y)
63
+ )
64
+ end
65
+ end
66
+
67
+ def position_from_percentages(position_in_percentages)
68
+ Position.new(
69
+ position_in_percentages.x / 100.0 * Field::WIDTH,
70
+ position_in_percentages.y / 100.0 * Field::HEIGHT
71
+ )
72
+ end
73
+
74
+ def position_to_percentages(position)
75
+ Position.new(
76
+ position.x / Field::WIDTH * 100,
77
+ position.y / Field::HEIGHT * 100
78
+ )
79
+ end
80
+
81
+ def position_side(position)
82
+ position.x < center_position.x ? :home : :away
83
+ end
84
+
85
+ def out_of_bounds_width?(position)
86
+ lower_limit = OFFSET.x
87
+ upper_limit = OFFSET.x + WIDTH
88
+ !(lower_limit..upper_limit).include?(position.x)
89
+ end
90
+
91
+ def out_of_bounds_height?(position)
92
+ lower_limit = OFFSET.y
93
+ upper_limit = OFFSET.y + HEIGHT
94
+ !(lower_limit..upper_limit).include?(position.y)
95
+ end
96
+
97
+ def goal?(position)
98
+ if out_of_bounds_width?(position)
99
+ lower_limit = center_position.y - GOAL_HEIGHT / 2
100
+ upper_limit = center_position.y + GOAL_HEIGHT / 2
101
+
102
+ (lower_limit..upper_limit).include?(position.y)
103
+ else
104
+ false
105
+ end
106
+ end
107
+
108
+ def close_to_goal?(position, side)
109
+ goal_position = Field.goal_position(side)
110
+ goal_position.distance(position) < CLOSE_GOAL_DISTANCE
111
+ end
112
+
113
+ def default_player_field_positions
114
+ defenders = Field::WIDTH / 6
115
+ midfielders = Field::WIDTH / 2
116
+ attackers = Field::WIDTH * 5 / 6
117
+
118
+ [
119
+ Position.new(50, Field::HEIGHT / 2),
120
+ Position.new(defenders, Field::HEIGHT / 5),
121
+ Position.new(defenders, Field::HEIGHT * 2 / 5),
122
+ Position.new(defenders, Field::HEIGHT * 3 / 5),
123
+ Position.new(defenders, Field::HEIGHT * 4 / 5),
124
+ Position.new(midfielders, Field::HEIGHT / 5),
125
+ Position.new(midfielders, Field::HEIGHT * 2 / 5),
126
+ Position.new(midfielders, Field::HEIGHT * 3 / 5),
127
+ Position.new(midfielders, Field::HEIGHT * 4 / 5),
128
+ Position.new(attackers, Field::HEIGHT / 3),
129
+ Position.new(attackers, Field::HEIGHT * 2 / 3)
130
+ ]
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,73 @@
1
+ require 'rubygoal/formation/formation_dsl'
2
+
3
+ module Rubygoal
4
+ class Formation
5
+ attr_accessor :players_position, :lines_definition
6
+
7
+ def initialize
8
+ @players_position = {}
9
+
10
+ @lines_definition = {
11
+ defenders: Field::WIDTH / 6,
12
+ def_midfielders: Field::WIDTH / 3,
13
+ midfielders: Field::WIDTH / 2,
14
+ att_midfielders: Field::WIDTH / 3 * 2,
15
+ attackers: Field::WIDTH / 6 * 5,
16
+ }
17
+ end
18
+
19
+ def method_missing(method, *args)
20
+ line_name = method.to_s.chomp('=').to_sym
21
+ if lines_definition[line_name]
22
+ set_players_in_custom_line(lines_definition[line_name], args)
23
+ end
24
+ end
25
+
26
+ def lineup(&block)
27
+ instance_eval(&block)
28
+ end
29
+
30
+ def errors
31
+ errors = []
32
+
33
+ # TODO Check if we need to count for the goalkeeper as well
34
+ if players_position.size != 10
35
+ errors << 'Incorrect number of players, are you missing a name?'
36
+ end
37
+
38
+ errors
39
+ end
40
+
41
+ def valid?
42
+ errors.empty?
43
+ end
44
+
45
+ private
46
+
47
+ def lines(&block)
48
+ CustomLines.apply(self, &block)
49
+ end
50
+
51
+ def custom_position(&block)
52
+ CustomPosition.apply(self, &block)
53
+ end
54
+
55
+ def set_players_in_custom_line(position_x, players)
56
+ base_position = Position.new(position_x, 0)
57
+ separation = line_position_separation(players)
58
+
59
+ players.each_with_index do |player, i|
60
+ next if player == :none
61
+
62
+ offset = Position.new(0, separation * (i + 1))
63
+ position = base_position.add(offset)
64
+
65
+ self.players_position[player] = position
66
+ end
67
+ end
68
+
69
+ def line_position_separation(players)
70
+ Field::HEIGHT / (players.size + 1)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,67 @@
1
+ module Rubygoal
2
+ class Formation
3
+ class FormationDSL
4
+ def self.apply(formation, &block)
5
+ dsl = self.new(formation)
6
+ dsl.instance_eval(&block)
7
+ dsl.apply
8
+ end
9
+
10
+ private
11
+
12
+ def field_width
13
+ 100.0
14
+ end
15
+
16
+ def field_height
17
+ 100.0
18
+ end
19
+ end
20
+
21
+ class CustomLines < FormationDSL
22
+ def initialize(formation)
23
+ @formation = formation
24
+ @lines = {}
25
+ end
26
+
27
+ def apply
28
+ formation.lines_definition.merge!(lines)
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :formation, :lines
34
+
35
+ def method_missing(method, *args)
36
+ define_line(method, args.first)
37
+ end
38
+
39
+ def define_line(name, x_position)
40
+ lines[name] = x_position / 100.0 * Field::WIDTH
41
+ end
42
+ end
43
+
44
+ class CustomPosition < FormationDSL
45
+ def initialize(formation)
46
+ @formation = formation
47
+ end
48
+
49
+ def apply
50
+ formation.players_position[player_name] = player_position
51
+ end
52
+
53
+ private
54
+
55
+ attr_reader :formation
56
+ attr_accessor :player_name, :player_position
57
+
58
+ def player(name)
59
+ self.player_name = name
60
+ end
61
+
62
+ def position(x, y)
63
+ self.player_position = Field.position_from_percentages(Position.new(x, y))
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,162 @@
1
+ require 'forwardable'
2
+
3
+ require 'rubygoal/coordinate'
4
+ require 'rubygoal/field'
5
+ require 'rubygoal/goal'
6
+ require 'rubygoal/recorder'
7
+
8
+ module Rubygoal
9
+ class Game
10
+ attr_reader :team_home, :team_away, :ball,
11
+ :time, :goal, :recorder,
12
+ :coach_home, :coach_away,
13
+ :score_home, :score_away
14
+
15
+ def initialize(coach_home, coach_away)
16
+ @coach_home = coach_home
17
+ @coach_away = coach_away
18
+
19
+ if debug_output?
20
+ puts "Home coach: #{@coach_home.name}"
21
+ puts "Away coach: #{@coach_away.name}"
22
+ end
23
+
24
+ @recorder = Recorder.new(self) if record_game?
25
+
26
+ @ball = Ball.new
27
+
28
+ @team_home = HomeTeam.new(self, coach_home)
29
+ @team_away = AwayTeam.new(self, coach_away)
30
+
31
+ @goal = Goal.new
32
+
33
+ @state = :playing
34
+
35
+ @time = Rubygoal.configuration.game_time
36
+ @score_home = 0
37
+ @score_away = 0
38
+
39
+ reinitialize_players
40
+ end
41
+
42
+ def update
43
+ return if ended?
44
+
45
+ update_elapsed_time
46
+
47
+ if celebrating_goal?
48
+ update_goal
49
+ else
50
+ update_remaining_time
51
+ team_home.update(elapsed_time)
52
+ team_away.update(elapsed_time)
53
+ update_ball
54
+ end
55
+
56
+ recorder.update if record_game?
57
+
58
+ end_match! if time <= 0
59
+ end
60
+
61
+ def players
62
+ teams.map(&:players_list).flatten
63
+ end
64
+
65
+ def celebrating_goal?
66
+ goal.celebrating?
67
+ end
68
+
69
+ def home_players_positions
70
+ team_home.players_position
71
+ end
72
+
73
+ def away_players_positions
74
+ team_away.players_position
75
+ end
76
+
77
+ def ball_position
78
+ ball.position
79
+ end
80
+
81
+ def recorded_game
82
+ recorder.to_hash if record_game?
83
+ end
84
+
85
+ def ended?
86
+ state == :ended
87
+ end
88
+
89
+ protected
90
+
91
+ attr_writer :time, :score_home, :score_away
92
+ attr_accessor :state, :last_time, :elapsed_time
93
+
94
+ private
95
+
96
+ def update_elapsed_time
97
+ self.last_time ||= Time.now
98
+
99
+ self.elapsed_time = Time.now - last_time
100
+ self.last_time = Time.now
101
+ end
102
+
103
+ def update_remaining_time
104
+ self.time -= elapsed_time
105
+ end
106
+
107
+ def update_ball
108
+ ball.update(elapsed_time)
109
+ if ball.goal?
110
+ update_score
111
+ goal.start_celebration
112
+ end
113
+ end
114
+
115
+ def update_goal
116
+ goal.update(elapsed_time)
117
+ reinitialize_match unless goal.celebrating?
118
+ end
119
+
120
+ def reinitialize_match
121
+ reinitialize_players
122
+ reinitialize_ball
123
+ end
124
+
125
+ def update_score
126
+ if Field.position_side(ball_position) == :home
127
+ self.score_away += 1
128
+ else
129
+ self.score_home += 1
130
+ end
131
+ end
132
+
133
+ def reinitialize_players
134
+ teams.each(&:players_to_initial_position)
135
+ end
136
+
137
+ def reinitialize_ball
138
+ ball.position = Field.center_position
139
+ end
140
+
141
+ def teams
142
+ [team_home, team_away]
143
+ end
144
+
145
+ def end_match!
146
+ self.state = :ended
147
+ puts_score if debug_output?
148
+ end
149
+
150
+ def puts_score
151
+ puts "#{coach_home.name} #{score_home} - #{score_away} #{coach_away.name}"
152
+ end
153
+
154
+ def debug_output?
155
+ Rubygoal.configuration.debug_output
156
+ end
157
+
158
+ def record_game?
159
+ Rubygoal.configuration.record_game
160
+ end
161
+ end
162
+ end