rubygoal 2.0.0 → 2.1.0

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.
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,53 @@
1
+ require 'rubygoal/game'
2
+
3
+ module Rubygoal
4
+ class Recorder
5
+ def initialize(game)
6
+ @game = game
7
+ @frames = []
8
+ end
9
+
10
+ def update
11
+ @frames << frame_info
12
+ end
13
+
14
+ def to_hash
15
+ {
16
+ teams: {
17
+ home: @game.coach_home.name,
18
+ away: @game.coach_away.name
19
+ },
20
+ score: [@game.score_home, @game.score_away],
21
+ frames: @frames
22
+ }
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :game, :frames
28
+
29
+ def frame_info
30
+ {
31
+ time: @game.time.round(0),
32
+ score: [@game.score_home, @game.score_away],
33
+ ball: [
34
+ @game.ball.position.x.round(0),
35
+ @game.ball.position.y.round(0)
36
+ ],
37
+ home: team_info(@game.team_home),
38
+ away: team_info(@game.team_away)
39
+ }
40
+ end
41
+
42
+ def team_info(team)
43
+ team.players.map do |_, player|
44
+ [
45
+ player.position.x.round(0),
46
+ player.position.y.round(0),
47
+ player.rotation.round(0),
48
+ player.type[0]
49
+ ]
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,33 @@
1
+ require 'timecop'
2
+ require 'json'
3
+ require 'rubygoal/game'
4
+
5
+ module Rubygoal
6
+ class Simulator
7
+ extend Forwardable
8
+ def_delegators :game, :recorded_game
9
+
10
+ def initialize
11
+ Rubygoal.configuration.record_game = true
12
+ @game = Rubygoal::Game.new(load_coach(:home), load_coach(:away))
13
+ end
14
+
15
+ def simulate
16
+ time = Time.now
17
+
18
+ while !game.ended? do
19
+ game.update
20
+ time += 1.0 / 60.0
21
+ Timecop.travel(time)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :game
28
+
29
+ def load_coach(side)
30
+ CoachLoader.new(side).coach
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,198 @@
1
+ require 'forwardable'
2
+
3
+ require 'rubygoal/formation'
4
+ require 'rubygoal/field'
5
+ require 'rubygoal/players/average'
6
+ require 'rubygoal/players/fast'
7
+ require 'rubygoal/players/captain'
8
+ require 'rubygoal/players/goalkeeper'
9
+ require 'rubygoal/match_data'
10
+
11
+ module Rubygoal
12
+ class Team
13
+ attr_reader :players, :side, :opponent_side, :coach, :formation
14
+ attr_accessor :goalkeeper
15
+
16
+ INFINITE = 100_000
17
+
18
+ extend Forwardable
19
+ def_delegators :coach, :name
20
+ def_delegators :game, :ball
21
+
22
+ def initialize(game, coach)
23
+ @game = game
24
+ @players = {}
25
+ @coach = coach
26
+
27
+ @match_data_factory = MatchData::Factory.new(game, side)
28
+
29
+ initialize_lineup_values
30
+ initialize_players
31
+ initialize_formation
32
+ end
33
+
34
+ def players_to_initial_position
35
+ match_data = match_data_factory.create
36
+ formation = coach.formation(match_data)
37
+ restart_player_positions_in_own_field(formation)
38
+ end
39
+
40
+ def update(elapsed_time)
41
+ match_data = match_data_factory.create
42
+ self.formation = coach.formation(match_data)
43
+
44
+ unless formation.valid?
45
+ puts formation.errors
46
+ raise "Invalid formation: #{coach.name}"
47
+ end
48
+
49
+ update_coach_defined_positions(formation)
50
+
51
+ player_to_move = nil
52
+ min_distance_to_ball = INFINITE
53
+ players_list.each do |player|
54
+ pass_or_shoot(player) if player.can_kick?(ball)
55
+
56
+ distance_to_ball = player.distance(ball.position)
57
+ if min_distance_to_ball > distance_to_ball
58
+ min_distance_to_ball = distance_to_ball
59
+ player_to_move = player
60
+ end
61
+ end
62
+
63
+ player_to_move.move_to(ball.position)
64
+
65
+ players.each do |name, player|
66
+ if name == :goalkeeper
67
+ if player != player_to_move
68
+ player.move_to_cover_goal(ball)
69
+ player.update(elapsed_time)
70
+ next
71
+ end
72
+ end
73
+
74
+ player.move_to_coach_position unless player == player_to_move
75
+ player.update(elapsed_time)
76
+ end
77
+ end
78
+
79
+ def players_list
80
+ players.values
81
+ end
82
+
83
+ def players_position
84
+ players.each_with_object({}) do |(name, player), hash|
85
+ hash[name] = Field.field_position(player.position, side)
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ attr_reader :game, :match_data_factory
92
+ attr_writer :formation
93
+
94
+ def initialize_lineup_values
95
+ @average_players_count = 6
96
+ @fast_players_count = 3
97
+ end
98
+
99
+ def initialize_formation
100
+ @formation = @coach.initial_formation
101
+ end
102
+
103
+ def initialize_players
104
+ @players = { goalkeeper: GoalKeeperPlayer.new(game, side) }
105
+
106
+ unless @coach.valid?
107
+ puts @coach.errors
108
+ raise "Invalid team definition: #{@coach.name}"
109
+ end
110
+
111
+ @players[@coach.captain_player.name] = CaptainPlayer.new(game, side)
112
+
113
+ @coach.players_by_type(:fast).each do |player_def|
114
+ @players[player_def.name] = FastPlayer.new(game, side)
115
+ end
116
+
117
+ @coach.players_by_type(:average).each do |player_def|
118
+ @players[player_def.name] = AveragePlayer.new(game, side)
119
+ end
120
+
121
+ initialize_player_positions
122
+ end
123
+
124
+ def pass_or_shoot(player)
125
+ # Kick straight to the goal whether the distance is short (200)
126
+ # or we don't have a better option
127
+ target = shoot_target
128
+
129
+ unless Field.close_to_goal?(player.position, opponent_side)
130
+ if teammate = nearest_forward_teammate(player)
131
+ target = teammate.position
132
+ end
133
+ end
134
+
135
+ player.kick(ball, target)
136
+ end
137
+
138
+ def nearest_forward_teammate(player)
139
+ min_dist = INFINITE
140
+ nearest_teammate = nil
141
+
142
+ (players.values - [player]).each do |teammate|
143
+ if teammate_is_on_front?(player, teammate)
144
+ dist = player.distance(teammate.position)
145
+ if min_dist > dist
146
+ nearest_teammate = teammate
147
+ min_dist = dist
148
+ end
149
+ end
150
+ end
151
+
152
+ nearest_teammate
153
+ end
154
+
155
+ def shoot_target
156
+ # Do not kick always to the center, look for the sides of the goal
157
+ limit = Field::GOAL_HEIGHT / 2
158
+ offset = Random.rand(-limit..limit)
159
+
160
+ target = Field.goal_position(opponent_side)
161
+ target.y += offset
162
+ target
163
+ end
164
+
165
+ def initialize_player_positions
166
+ Field.default_player_field_positions.each_with_index do |pos, index|
167
+ players.values[index].position = lineup_to_position(pos)
168
+ players.values[index].coach_defined_position = lineup_to_position(pos)
169
+ end
170
+ end
171
+
172
+ def update_coach_defined_positions(formation)
173
+ formation.players_position.each do |player_name, pos|
174
+ players[player_name].coach_defined_position = lineup_to_position(pos)
175
+ end
176
+ end
177
+
178
+ def restart_player_positions_in_own_field(formation)
179
+ formation.players_position.each do |player_name, pos|
180
+ pos.x *= 0.5
181
+ pos = lineup_to_position(pos)
182
+
183
+ player = players[player_name]
184
+
185
+ player.coach_defined_position = pos
186
+ player.position = pos
187
+ end
188
+ end
189
+
190
+ def lineup_to_position(field_position)
191
+ Field.absolute_position(field_position, side)
192
+ end
193
+
194
+ def goalkeeper
195
+ players[:goalkeeper]
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,15 @@
1
+ require 'rubygoal/team'
2
+
3
+ module Rubygoal
4
+ class AwayTeam < Team
5
+ def initialize(*args)
6
+ @side = :away
7
+ @opponent_side = :home
8
+ super
9
+ end
10
+
11
+ def teammate_is_on_front?(player, teammate)
12
+ teammate.position.x < player.position.x - 40
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ require 'rubygoal/team'
2
+
3
+ module Rubygoal
4
+ class HomeTeam < Team
5
+ def initialize(*args)
6
+ @side = :home
7
+ @opponent_side = :away
8
+ super
9
+ end
10
+
11
+ def teammate_is_on_front?(player, teammate)
12
+ teammate.position.x > player.position.x + 40
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,36 @@
1
+ module Rubygoal
2
+ module Util
3
+ class << self
4
+ def offset_x(angle, distance)
5
+ distance * Math.cos(angle * Math::PI / 180.0)
6
+ end
7
+
8
+ def offset_y(angle, distance)
9
+ distance * Math.sin(angle * Math::PI / 180.0)
10
+ end
11
+
12
+ def distance(x1, y1, x2, y2)
13
+ Math.hypot(x2 - x1, y2 - y1)
14
+ end
15
+
16
+ def angle(x1, y1, x2, y2)
17
+ Math.atan2(y2 - y1, x2 - x1) / Math::PI * 180.0
18
+ end
19
+
20
+ def positive_angle(x1, y1, x2, y2)
21
+ angle = self.angle(x1, y1, x2, y2)
22
+ if angle < 0
23
+ 360 + angle
24
+ else
25
+ angle
26
+ end
27
+ end
28
+
29
+ def y_intercept_with_line(x, pos1, pos2)
30
+ slope = (pos2.y - pos1.y) / (pos2.x - pos1.x)
31
+
32
+ Position.new(x, slope * (x - pos1.x) + pos1.y)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,3 @@
1
+ module Rubygoal
2
+ VERSION = Gem::Version.new '1.1.0'
3
+ end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubygoal
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jorge Bejar
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-01 00:00:00.000000000 Z
11
+ date: 2017-08-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubygoal-core
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '='
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 1.0.0
19
+ version: '1.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '='
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 1.0.0
26
+ version: '1.1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: gosu
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -50,22 +50,34 @@ files:
50
50
  - README.md
51
51
  - bin/rubygoal
52
52
  - lib/rubygoal.rb
53
- - lib/rubygoal/gui.rb
54
- - lib/rubygoal/gui/ball.rb
55
- - lib/rubygoal/gui/field.rb
56
- - lib/rubygoal/gui/game.rb
57
- - lib/rubygoal/gui/goal.rb
58
- - lib/rubygoal/gui/players.rb
59
- - lib/rubygoal/gui/version.rb
60
- - media/average_away.png
61
- - media/average_home.png
62
- - media/background.png
63
- - media/ball.png
64
- - media/captain_away.png
65
- - media/captain_home.png
66
- - media/fast_away.png
67
- - media/fast_home.png
68
- - media/goal.png
53
+ - lib/rubygoal/ball.rb
54
+ - lib/rubygoal/coach.rb
55
+ - lib/rubygoal/coach_definition.rb
56
+ - lib/rubygoal/coach_loader.rb
57
+ - lib/rubygoal/coaches/coach_definition_away.rb
58
+ - lib/rubygoal/coaches/coach_definition_home.rb
59
+ - lib/rubygoal/configuration.rb
60
+ - lib/rubygoal/coordinate.rb
61
+ - lib/rubygoal/field.rb
62
+ - lib/rubygoal/formation.rb
63
+ - lib/rubygoal/formation/formation_dsl.rb
64
+ - lib/rubygoal/game.rb
65
+ - lib/rubygoal/goal.rb
66
+ - lib/rubygoal/match_data.rb
67
+ - lib/rubygoal/moveable.rb
68
+ - lib/rubygoal/player.rb
69
+ - lib/rubygoal/players/average.rb
70
+ - lib/rubygoal/players/captain.rb
71
+ - lib/rubygoal/players/fast.rb
72
+ - lib/rubygoal/players/goalkeeper.rb
73
+ - lib/rubygoal/players/player_movement.rb
74
+ - lib/rubygoal/recorder.rb
75
+ - lib/rubygoal/simulator.rb
76
+ - lib/rubygoal/team.rb
77
+ - lib/rubygoal/teams/away.rb
78
+ - lib/rubygoal/teams/home.rb
79
+ - lib/rubygoal/util.rb
80
+ - lib/rubygoal/version.rb
69
81
  homepage: https://github.com/wyeworks/rubygoal
70
82
  licenses:
71
83
  - Apache License 2.0
@@ -86,7 +98,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
86
98
  version: '0'
87
99
  requirements: []
88
100
  rubyforge_project:
89
- rubygems_version: 2.4.5
101
+ rubygems_version: 2.6.11
90
102
  signing_key:
91
103
  specification_version: 4
92
104
  summary: Rubygoal Game - Soccer game for Rubysts