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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +24 -0
- data/README.md +62 -0
- data/bin/rubygoal +5 -0
- data/lib/rubygoal/ball.rb +41 -0
- data/lib/rubygoal/coach.rb +11 -0
- data/lib/rubygoal/coach_loader.rb +32 -0
- data/lib/rubygoal/coaches/coach_away.rb +52 -0
- data/lib/rubygoal/coaches/coach_home.rb +28 -0
- data/lib/rubygoal/coaches/template.rb +198 -0
- data/lib/rubygoal/config.rb +64 -0
- data/lib/rubygoal/config_definitions.rb +77 -0
- data/lib/rubygoal/coordinate.rb +25 -0
- data/lib/rubygoal/field.rb +84 -0
- data/lib/rubygoal/field_metrics.rb +85 -0
- data/lib/rubygoal/formation.rb +69 -0
- data/lib/rubygoal/game.rb +179 -0
- data/lib/rubygoal/goal.rb +43 -0
- data/lib/rubygoal/match.rb +52 -0
- data/lib/rubygoal/moveable.rb +55 -0
- data/lib/rubygoal/player.rb +86 -0
- data/lib/rubygoal/players/average.rb +20 -0
- data/lib/rubygoal/players/captain.rb +20 -0
- data/lib/rubygoal/players/fast.rb +20 -0
- data/lib/rubygoal/team.rb +217 -0
- data/lib/rubygoal/version.rb +3 -0
- data/lib/rubygoal.rb +7 -0
- data/media/average_away.png +0 -0
- data/media/average_home.png +0 -0
- data/media/background.png +0 -0
- data/media/ball.png +0 -0
- data/media/captain_away.png +0 -0
- data/media/captain_home.png +0 -0
- data/media/fast_away.png +0 -0
- data/media/fast_home.png +0 -0
- data/media/goal.png +0 -0
- data/test/formation_test.rb +66 -0
- data/test/random.txt +788 -0
- data/test/record.txt +432000 -0
- data/test/test.rb +104 -0
- metadata +116 -0
@@ -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
|
data/lib/rubygoal.rb
ADDED
Binary file
|
Binary file
|
Binary file
|
data/media/ball.png
ADDED
Binary file
|
Binary file
|
Binary file
|
data/media/fast_away.png
ADDED
Binary file
|
data/media/fast_home.png
ADDED
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
|