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.
- checksums.yaml +4 -4
- data/bin/rubygoal +12 -2
- data/lib/rubygoal.rb +1 -18
- data/lib/rubygoal/ball.rb +60 -0
- data/lib/rubygoal/coach.rb +81 -0
- data/lib/rubygoal/coach_definition.rb +54 -0
- data/lib/rubygoal/coach_loader.rb +55 -0
- data/lib/rubygoal/coaches/coach_definition_away.rb +72 -0
- data/lib/rubygoal/coaches/coach_definition_home.rb +76 -0
- data/lib/rubygoal/configuration.rb +49 -0
- data/lib/rubygoal/coordinate.rb +33 -0
- data/lib/rubygoal/field.rb +134 -0
- data/lib/rubygoal/formation.rb +73 -0
- data/lib/rubygoal/formation/formation_dsl.rb +67 -0
- data/lib/rubygoal/game.rb +162 -0
- data/lib/rubygoal/goal.rb +26 -0
- data/lib/rubygoal/match_data.rb +130 -0
- data/lib/rubygoal/moveable.rb +68 -0
- data/lib/rubygoal/player.rb +87 -0
- data/lib/rubygoal/players/average.rb +16 -0
- data/lib/rubygoal/players/captain.rb +15 -0
- data/lib/rubygoal/players/fast.rb +16 -0
- data/lib/rubygoal/players/goalkeeper.rb +26 -0
- data/lib/rubygoal/players/player_movement.rb +98 -0
- data/lib/rubygoal/recorder.rb +53 -0
- data/lib/rubygoal/simulator.rb +33 -0
- data/lib/rubygoal/team.rb +198 -0
- data/lib/rubygoal/teams/away.rb +15 -0
- data/lib/rubygoal/teams/home.rb +15 -0
- data/lib/rubygoal/util.rb +36 -0
- data/lib/rubygoal/version.rb +3 -0
- metadata +35 -23
- data/lib/rubygoal/gui.rb +0 -20
- data/lib/rubygoal/gui/ball.rb +0 -26
- data/lib/rubygoal/gui/field.rb +0 -21
- data/lib/rubygoal/gui/game.rb +0 -166
- data/lib/rubygoal/gui/goal.rb +0 -23
- data/lib/rubygoal/gui/players.rb +0 -28
- data/lib/rubygoal/gui/version.rb +0 -5
- 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
@@ -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
|
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.
|
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:
|
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.
|
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.
|
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/
|
54
|
-
- lib/rubygoal/
|
55
|
-
- lib/rubygoal/
|
56
|
-
- lib/rubygoal/
|
57
|
-
- lib/rubygoal/
|
58
|
-
- lib/rubygoal/
|
59
|
-
- lib/rubygoal/
|
60
|
-
-
|
61
|
-
-
|
62
|
-
-
|
63
|
-
-
|
64
|
-
-
|
65
|
-
-
|
66
|
-
-
|
67
|
-
-
|
68
|
-
-
|
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.
|
101
|
+
rubygems_version: 2.6.11
|
90
102
|
signing_key:
|
91
103
|
specification_version: 4
|
92
104
|
summary: Rubygoal Game - Soccer game for Rubysts
|