rubygoal 0.0.1

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.
@@ -0,0 +1,217 @@
1
+ require 'forwardable'
2
+
3
+ require 'rubygoal/config'
4
+ require 'rubygoal/formation'
5
+ require 'rubygoal/field_metrics'
6
+ require 'rubygoal/player'
7
+
8
+ module Rubygoal
9
+ class Team
10
+ attr_reader :game, :players, :side, :coach, :formation
11
+ attr_accessor :goalkeeper, :positions
12
+
13
+ INFINITE = 100_000
14
+
15
+ extend Forwardable
16
+ def_delegators :coach, :name
17
+
18
+ def initialize(game_window, side, coach)
19
+ @game = game_window
20
+ @players = []
21
+ @side = side
22
+ @coach = coach
23
+
24
+ initialize_formation
25
+ initialize_players(game_window)
26
+ end
27
+
28
+ def players_to_initial_position
29
+ positions = FieldMetrics.initial_player_positions(side)
30
+ players.each_with_index do |player, index|
31
+ player.position = positions[index]
32
+ end
33
+ end
34
+
35
+ def update(match)
36
+ self.formation = coach.formation(match)
37
+ unless formation.valid?
38
+ puts formation.errors
39
+ raise "Invalid formation: #{coach.name}"
40
+ end
41
+ update_positions(formation.lineup)
42
+
43
+ player_to_move = nil
44
+ min_distance_to_ball = INFINITE
45
+ players.each do |player|
46
+ pass_or_shoot(player) if player.can_kick?(game.ball)
47
+
48
+ distance_to_ball = player.distance(game.ball.position)
49
+ if min_distance_to_ball > distance_to_ball
50
+ min_distance_to_ball = distance_to_ball
51
+ player_to_move = player
52
+ end
53
+ end
54
+
55
+ player_to_move.move_to(game.ball.position)
56
+
57
+ average_players = []
58
+ fast_players = []
59
+ captain_player = nil
60
+
61
+ players.each do |player|
62
+ if player.is_a? AveragePlayer
63
+ average_players << player
64
+ elsif player.is_a? FastPlayer
65
+ fast_players << player
66
+ else
67
+ captain_player = player
68
+ end
69
+ end
70
+
71
+ if captain_player != player_to_move
72
+ captain_player.move_to(positions[:captain])
73
+ end
74
+ captain_player.update
75
+
76
+ average_players.each_with_index do |player, index|
77
+ if player != player_to_move
78
+ player.move_to(positions[:average][index])
79
+ end
80
+
81
+ player.update
82
+ end
83
+
84
+ fast_players.each_with_index do |player, index|
85
+ if player != player_to_move
86
+ player.move_to(positions[:fast][index])
87
+ end
88
+
89
+ player.update
90
+ end
91
+ end
92
+
93
+ def draw
94
+ players.each(&:draw)
95
+ end
96
+
97
+ private
98
+
99
+ attr_writer :formation
100
+
101
+ def initialize_formation
102
+ @formation = Formation.new
103
+ @formation.lineup = [
104
+ [:average, :none, :average, :none, :none ],
105
+ [:average, :none, :fast, :none, :captain],
106
+ [:none, :none, :none, :none, :none ],
107
+ [:average, :none, :fast, :none, :fast ],
108
+ [:average, :none, :average, :none, :none ]
109
+ ]
110
+ end
111
+
112
+ def initialize_players(game_window)
113
+ Config.team.average_players_count.times do
114
+ players << AveragePlayer.new(game_window, side)
115
+ end
116
+ Config.team.fast_players_count.times do
117
+ players << FastPlayer.new(game_window, side)
118
+ end
119
+ Config.team.captain_players_count.times do
120
+ players << CaptainPlayer.new(game_window, side)
121
+ end
122
+
123
+ players_to_initial_position
124
+ end
125
+
126
+ def pass_or_shoot(player)
127
+ # Kick straight to the goal whether the distance is short (200)
128
+ # or we don't have a better option
129
+ target = shoot_target
130
+
131
+ unless FieldMetrics.close_to_goal?(player.position, opponent_side)
132
+ if teammate = nearest_forward_teammate(player)
133
+ target = teammate.position
134
+ end
135
+ end
136
+
137
+ player.kick(game.ball, target)
138
+ end
139
+
140
+ def nearest_forward_teammate(player)
141
+ min_dist = INFINITE
142
+ nearest_teammate = nil
143
+
144
+ (players - [player]).each do |teammate|
145
+ if side == :home
146
+ is_forward = teammate.position.x > player.position.x + 40
147
+ else
148
+ is_forward = teammate.position.x < player.position.x - 40
149
+ end
150
+
151
+ if is_forward
152
+ dist = player.distance(teammate.position)
153
+ if min_dist > dist
154
+ nearest_teammate = teammate
155
+ min_dist = dist
156
+ end
157
+ end
158
+ end
159
+
160
+ nearest_teammate
161
+ end
162
+
163
+ def opponent_side
164
+ if side == :home
165
+ :away
166
+ else
167
+ :home
168
+ end
169
+ end
170
+
171
+ def shoot_target
172
+ # Do not kick always to the center, look for the sides of the goal
173
+ limit = Config.field.goal_height / 2
174
+ offset = Random.rand(-limit..limit)
175
+
176
+ target = FieldMetrics.goal_position(opponent_side)
177
+ target.y += offset
178
+ target
179
+ end
180
+
181
+ def update_positions(lineup)
182
+ self.positions = {
183
+ average: [FieldMetrics.goalkeeper_position(side)],
184
+ fast: []
185
+ }
186
+
187
+ lineup.each_with_index do |row, i|
188
+ row.each_with_index do |player, j|
189
+ if player == :average
190
+ positions[:average] << matrix_to_position(i, j)
191
+ elsif player == :captain
192
+ positions[:captain] = matrix_to_position(i, j)
193
+ elsif player == :fast
194
+ positions[:fast] << matrix_to_position(i, j)
195
+ end
196
+ end
197
+ end
198
+ end
199
+
200
+ def matrix_to_position(i, j)
201
+ step_x = Config.field.width / 6
202
+ step_y = Config.field.height / 6
203
+ if side == :home
204
+ Config.field.offset.add(
205
+ Position.new((j+1) * step_x - 30, (i+1) * step_y)
206
+ )
207
+ else
208
+ Config.field.offset.add(
209
+ Position.new(
210
+ Config.field.width - (j+1) * step_x + 30,
211
+ Config.field.height - (i+1) * step_y
212
+ )
213
+ )
214
+ end
215
+ end
216
+ end
217
+ end
@@ -0,0 +1,3 @@
1
+ module Rubygoal
2
+ VERSION = Gem::Version.new '0.0.1'
3
+ end
data/lib/rubygoal.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'rubygoal/game'
2
+
3
+ module Rubygoal
4
+ def self.start
5
+ Game.new.show
6
+ end
7
+ end
Binary file
Binary file
Binary file
data/media/ball.png ADDED
Binary file
Binary file
Binary file
Binary file
Binary file
data/media/goal.png ADDED
Binary file
@@ -0,0 +1,66 @@
1
+ require 'minitest/autorun'
2
+
3
+ require 'rubygoal/formation'
4
+
5
+ module Rubygoal
6
+ class TestFormation < Minitest::Test
7
+ def setup
8
+ @formation = Formation.new
9
+ end
10
+
11
+ def test_valid_formation
12
+ @formation.lineup = [
13
+ [:average, :none, :average, :none, :none],
14
+ [:fast, :none, :none, :average, :none],
15
+ [:none, :none, :captain, :none, :fast],
16
+ [:fast, :none, :none, :none, :average],
17
+ [:average, :none, :average, :none, :none],
18
+ ]
19
+ assert_equal @formation.valid?, true
20
+ end
21
+
22
+ def test_less_players
23
+ @formation.lineup = [
24
+ [:average, :none, :average, :none, :none],
25
+ [:fast, :none, :none, :none, :none],
26
+ [:none, :none, :captain, :none, :fast],
27
+ [:fast, :none, :none, :none, :average],
28
+ [:average, :none, :average, :none, :none],
29
+ ]
30
+ assert_equal @formation.valid?, false
31
+ end
32
+
33
+ def test_more_players
34
+ @formation.lineup = [
35
+ [:average, :none, :average, :none, :average],
36
+ [:fast, :none, :none, :average, :none],
37
+ [:none, :none, :captain, :none, :fast],
38
+ [:fast, :none, :none, :none, :average],
39
+ [:average, :none, :average, :none, :none],
40
+ ]
41
+ assert_equal @formation.valid?, false
42
+ end
43
+
44
+ def test_more_than_one_captain
45
+ @formation.lineup = [
46
+ [:average, :none, :average, :none, :none],
47
+ [:fast, :none, :none, :captain, :none],
48
+ [:none, :none, :captain, :none, :fast],
49
+ [:fast, :none, :none, :none, :average],
50
+ [:average, :none, :average, :none, :none],
51
+ ]
52
+ assert_equal @formation.valid?, false
53
+ end
54
+
55
+ def test_more_than_three_fast
56
+ @formation.lineup = [
57
+ [:average, :none, :average, :none, :none],
58
+ [:fast, :none, :none, :fast, :none],
59
+ [:none, :none, :captain, :none, :fast],
60
+ [:fast, :none, :none, :none, :average],
61
+ [:average, :none, :average, :none, :none],
62
+ ]
63
+ assert_equal @formation.valid?, false
64
+ end
65
+ end
66
+ end