rubygoal-core 1.0.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 +7 -0
- data/LICENSE +18 -0
- data/README.md +85 -0
- data/bin/rubygoal +15 -0
- data/lib/rubygoal.rb +1 -0
- 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 +67 -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 +56 -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
- data/test/ball_test.rb +56 -0
- data/test/coach_test.rb +49 -0
- data/test/field_test.rb +103 -0
- data/test/fixtures/four_fast_players_coach_definition.rb +27 -0
- data/test/fixtures/less_players_coach_definition.rb +25 -0
- data/test/fixtures/mirror_strategy_coach_definition.rb +42 -0
- data/test/fixtures/more_players_coach_definition.rb +27 -0
- data/test/fixtures/test_away_coach_definition.rb +34 -0
- data/test/fixtures/test_home_coach_definition.rb +34 -0
- data/test/fixtures/two_captains_coach_definition.rb +27 -0
- data/test/fixtures/valid_coach_definition.rb +26 -0
- data/test/formation_test.rb +81 -0
- data/test/game_test.rb +84 -0
- data/test/match_data_test.rb +149 -0
- data/test/player_test.rb +125 -0
- data/test/players/goalkeeper_test.rb +49 -0
- data/test/players/player_movement_test.rb +76 -0
- data/test/recorder_test.rb +118 -0
- data/test/team_test.rb +97 -0
- data/test/test_helper.rb +21 -0
- metadata +158 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b9467e3e2d813c7e05c2b773fa9552c551c1b3d1
|
4
|
+
data.tar.gz: 547398aa354e4077b01b0348ce7f70cfca999880
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 878e490f660c88b9809959c5839833413b4d8dd5fbd7243615b82a2760d335467499c3cf8e03234442a385348665c452fe9bc1e46a132102b57a7cc5e25a11e7
|
7
|
+
data.tar.gz: 5497617e5e87057f6d235461a3a73f7e74cf53b88ce8eed99d4b583b74227aa78f321355dd27723527d6925ad2e715e0d179be84968f1f4dcd9d71fe63fe6fd2
|
data/LICENSE
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Copyright 2014 Jorge Bejar
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
14
|
+
|
15
|
+
-----
|
16
|
+
|
17
|
+
This does not apply to files under the "media/" folder. For licensing of
|
18
|
+
those files refer to the Legal section in the README.md file.
|
data/README.md
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
[](https://travis-ci.org/wyeworks/rubygoal)
|
2
|
+
[](https://codeclimate.com/github/wyeworks/rubygoal)
|
3
|
+
[](http://inch-ci.org/github/wyeworks/rubygoal)
|
4
|
+
|
5
|
+
#Welcome to RubyGoal!
|
6
|
+
|
7
|
+
## What is RubyGoal?
|
8
|
+
|
9
|
+
RubyGoal is a game in which you will be coaching your football team.
|
10
|
+
|
11
|
+
You will be coding your strategy in RUBY :D .
|
12
|
+
|
13
|
+
## Dependencies
|
14
|
+
|
15
|
+
GNU/Linux, Make sure you have all dependencies installed.
|
16
|
+
|
17
|
+
Ubuntu/Debian:
|
18
|
+
|
19
|
+
```bash
|
20
|
+
# Gosu's dependencies for both C++ and Ruby
|
21
|
+
sudo apt-get install build-essential libsdl2-dev libsdl2-ttf-dev
|
22
|
+
libpango1.0-dev \
|
23
|
+
libgl1-mesa-dev libfreeimage-dev libopenal-dev
|
24
|
+
libsndfile-dev
|
25
|
+
```
|
26
|
+
|
27
|
+
For other distros: https://github.com/jlnr/gosu/wiki/Getting-Started-on-Linux
|
28
|
+
|
29
|
+
## How do i run it?
|
30
|
+
|
31
|
+
```bash
|
32
|
+
gem install rubygoal
|
33
|
+
```
|
34
|
+
|
35
|
+
Run the game with example `CoachDefinition`
|
36
|
+
```bash
|
37
|
+
rubygoal
|
38
|
+
```
|
39
|
+
|
40
|
+
Run the game with your custom `CoachDefinition` implementation
|
41
|
+
```bash
|
42
|
+
rubygoal coach_1.rb
|
43
|
+
```
|
44
|
+
|
45
|
+
Run the game with your home and away `CoachDefinition` implementations
|
46
|
+
```bash
|
47
|
+
rubygoal coach_1.rb coach_2.rb
|
48
|
+
```
|
49
|
+
|
50
|
+
If you want to run the game from the source code, clone the project and
|
51
|
+
run the following commands:
|
52
|
+
|
53
|
+
```bash
|
54
|
+
BUNDLE_GEMFILE=Gemfile.dev bundle install
|
55
|
+
BUNDLE_GEMFILE=Gemfile.dev bundle exec ruby gui/bin/rubygoal [coach_file] [coach_file]
|
56
|
+
```
|
57
|
+
|
58
|
+
Also, you can simulate a game without the GUI by running
|
59
|
+
|
60
|
+
```bash
|
61
|
+
bundle install
|
62
|
+
bundle exec ruby bin/rubygoal [coach_file] [coach_file]
|
63
|
+
```
|
64
|
+
|
65
|
+
When you simuate a game, a JSON file is created in the same folder. You
|
66
|
+
could run this using our experiment webcomponent to play Rubygoal in the
|
67
|
+
web: https://github.com/jmbejar/rubygoal-webplayer
|
68
|
+
|
69
|
+
## How do i write my own coach class?
|
70
|
+
|
71
|
+
You can find a complete guide explaining how to program a coach in
|
72
|
+
www.rubygoal.com
|
73
|
+
|
74
|
+
Aditionally, you can take a look to the already defined `CoachDefinition` at
|
75
|
+
`lib/rubygoal/coach_definition`.
|
76
|
+
Specially pay attention to the example coaches in `lib/rubygoal/coaches/`
|
77
|
+
|
78
|
+
|
79
|
+
## Legal
|
80
|
+
All source code, except the files under the `media/` folder, is
|
81
|
+
licensed under the Apache License 2.0. Please see the `LICENSE` file under
|
82
|
+
the gem root folder.
|
83
|
+
|
84
|
+
All media files under the `media/` folder are licensed under the Creative
|
85
|
+
Commons 4.0 Attribution license. Please see https://creativecommons.org/licenses/by/4.0/
|
data/bin/rubygoal
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- mode: ruby -*-
|
3
|
+
|
4
|
+
require 'rubygoal/simulator'
|
5
|
+
|
6
|
+
simulator = Rubygoal::Simulator.new
|
7
|
+
simulator.simulate
|
8
|
+
|
9
|
+
timestamp = Time.now.strftime("%Y%m%d%H%M%S")
|
10
|
+
file = File.new("recorded_game_#{timestamp}.json", 'w')
|
11
|
+
JSON.dump(simulator.recorded_game, file)
|
12
|
+
|
13
|
+
p "Match recorded in #{file.path}"
|
14
|
+
|
15
|
+
|
data/lib/rubygoal.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'rubygoal/game'
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'rubygoal/util'
|
2
|
+
require 'rubygoal/field'
|
3
|
+
require 'rubygoal/moveable'
|
4
|
+
|
5
|
+
module Rubygoal
|
6
|
+
class Ball
|
7
|
+
include Moveable
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
super
|
11
|
+
reinitialize_position
|
12
|
+
end
|
13
|
+
|
14
|
+
def goal?
|
15
|
+
Field.goal?(position)
|
16
|
+
end
|
17
|
+
|
18
|
+
def move(direction, speed)
|
19
|
+
self.velocity = Velocity.new(
|
20
|
+
Util.offset_x(direction, speed),
|
21
|
+
Util.offset_y(direction, speed)
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
def reinitialize_position
|
26
|
+
self.position = Field.center_position
|
27
|
+
end
|
28
|
+
|
29
|
+
def update(elapsed_time)
|
30
|
+
super
|
31
|
+
|
32
|
+
prevent_out_of_bounds
|
33
|
+
decelerate(elapsed_time)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def prevent_out_of_bounds
|
39
|
+
if Field.out_of_bounds_width?(position)
|
40
|
+
velocity.x *= -1
|
41
|
+
end
|
42
|
+
if Field.out_of_bounds_height?(position)
|
43
|
+
velocity.y *= -1
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def decelerate(elapsed_time)
|
48
|
+
coef = deceleration_coef(elapsed_time)
|
49
|
+
|
50
|
+
self.velocity = velocity.mult(coef)
|
51
|
+
end
|
52
|
+
|
53
|
+
def deceleration_coef(elapsed_time)
|
54
|
+
custom_frame_rate = 1 / 60.0
|
55
|
+
time_coef = elapsed_time / custom_frame_rate
|
56
|
+
|
57
|
+
Rubygoal.configuration.deceleration_coef ** time_coef
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Rubygoal
|
4
|
+
class Coach
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
def_delegators :coach_definition, :players, :name, :formation
|
8
|
+
|
9
|
+
def initialize(coach_definition)
|
10
|
+
@coach_definition = coach_definition
|
11
|
+
end
|
12
|
+
|
13
|
+
def errors
|
14
|
+
[].tap do |errors|
|
15
|
+
check_unique_captain(errors)
|
16
|
+
check_players_count(:average, errors)
|
17
|
+
check_players_count(:fast, errors)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def valid?
|
22
|
+
errors.empty?
|
23
|
+
end
|
24
|
+
|
25
|
+
def players_by_type(type)
|
26
|
+
players.select { |p| p.type == type }
|
27
|
+
end
|
28
|
+
|
29
|
+
def captain_player
|
30
|
+
players_by_type(:captain).first
|
31
|
+
end
|
32
|
+
|
33
|
+
def fast_players
|
34
|
+
players_by_type(:fast)
|
35
|
+
end
|
36
|
+
|
37
|
+
def average_players
|
38
|
+
players_by_type(:average)
|
39
|
+
end
|
40
|
+
|
41
|
+
def initial_formation
|
42
|
+
average_names = average_players.map(&:name)
|
43
|
+
fast_names = fast_players.map(&:name)
|
44
|
+
captain_name = captain_player.name
|
45
|
+
|
46
|
+
formation = Formation.new
|
47
|
+
|
48
|
+
formation.lineup do
|
49
|
+
defenders average_names[0], average_names[2], :none, average_names[3], average_names[4]
|
50
|
+
midfielders average_names[1], fast_names[0], :none, fast_names[1], average_names[5]
|
51
|
+
attackers :none, captain_name, :none, fast_names[2], :none
|
52
|
+
end
|
53
|
+
|
54
|
+
formation
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
attr_reader :coach_definition
|
60
|
+
|
61
|
+
def game_config
|
62
|
+
Rubygoal.configuration
|
63
|
+
end
|
64
|
+
|
65
|
+
def check_unique_captain(errors)
|
66
|
+
captain_count = players_by_type(:captain).size
|
67
|
+
|
68
|
+
if captain_count != 1
|
69
|
+
errors << "The number of captains is #{captain_count}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def check_players_count(type, errors)
|
74
|
+
players_count = players_by_type(type).size
|
75
|
+
|
76
|
+
if players_count != game_config.send("#{type}_players_count")
|
77
|
+
errors << "The number of #{type} players is #{players_count}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Rubygoal
|
2
|
+
class CoachDefinition
|
3
|
+
PlayerDefinition = Struct.new(:name, :type)
|
4
|
+
|
5
|
+
class << self
|
6
|
+
attr_reader :team_name
|
7
|
+
|
8
|
+
def team(&block)
|
9
|
+
instance_eval(&block)
|
10
|
+
end
|
11
|
+
|
12
|
+
def name(team_name)
|
13
|
+
@team_name = team_name
|
14
|
+
end
|
15
|
+
|
16
|
+
def players(&block)
|
17
|
+
@team_players = []
|
18
|
+
instance_eval(&block)
|
19
|
+
end
|
20
|
+
|
21
|
+
def method_missing(method, *args)
|
22
|
+
name = args.first.to_sym
|
23
|
+
@team_players << PlayerDefinition.new(name, method.to_sym)
|
24
|
+
end
|
25
|
+
|
26
|
+
def team_players
|
27
|
+
@team_players || [
|
28
|
+
PlayerDefinition.new(:captain, :captain),
|
29
|
+
PlayerDefinition.new(:fast1, :fast),
|
30
|
+
PlayerDefinition.new(:fast2, :fast),
|
31
|
+
PlayerDefinition.new(:fast3, :fast),
|
32
|
+
PlayerDefinition.new(:average1, :average),
|
33
|
+
PlayerDefinition.new(:average2, :average),
|
34
|
+
PlayerDefinition.new(:average3, :average),
|
35
|
+
PlayerDefinition.new(:average4, :average),
|
36
|
+
PlayerDefinition.new(:average5, :average),
|
37
|
+
PlayerDefinition.new(:average6, :average),
|
38
|
+
]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def players
|
43
|
+
self.class.team_players
|
44
|
+
end
|
45
|
+
|
46
|
+
def name
|
47
|
+
self.class.team_name
|
48
|
+
end
|
49
|
+
|
50
|
+
def formation(match)
|
51
|
+
raise NotImplementedError
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'rubygoal/coach'
|
2
|
+
require 'rubygoal/coaches/coach_definition_home'
|
3
|
+
require 'rubygoal/coaches/coach_definition_away'
|
4
|
+
|
5
|
+
module Rubygoal
|
6
|
+
class CoachLoader
|
7
|
+
DEFAULT_COACH_DEFINITIONS = {
|
8
|
+
home: CoachDefinitionHome,
|
9
|
+
away: CoachDefinitionAway
|
10
|
+
}
|
11
|
+
|
12
|
+
def initialize(side)
|
13
|
+
@side = side
|
14
|
+
end
|
15
|
+
|
16
|
+
def coach
|
17
|
+
Coach.new(load_definition_coach)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :side
|
23
|
+
|
24
|
+
def default_definition_class
|
25
|
+
DEFAULT_COACH_DEFINITIONS[side]
|
26
|
+
end
|
27
|
+
|
28
|
+
def load_definition_coach
|
29
|
+
if filename && File.exists?(filename)
|
30
|
+
load filename
|
31
|
+
|
32
|
+
class_name = camelize(File.basename(filename, ".rb"))
|
33
|
+
Rubygoal.const_get(class_name).new
|
34
|
+
else
|
35
|
+
if filename && Rubygoal.configuration.debug_output
|
36
|
+
puts "File `#{filename}` doesn't exist. Using #{default_definition_class.name}."
|
37
|
+
end
|
38
|
+
|
39
|
+
default_definition_class.new
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def filename
|
44
|
+
side == :home ? ARGV[0] : ARGV[1]
|
45
|
+
end
|
46
|
+
|
47
|
+
def camelize(term)
|
48
|
+
string = term.to_s
|
49
|
+
string = string.sub(/^[a-z\d]*/) { $&.capitalize }
|
50
|
+
string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }
|
51
|
+
string.gsub!('/', '::')
|
52
|
+
string
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'rubygoal/coach_definition'
|
2
|
+
require 'rubygoal/formation'
|
3
|
+
|
4
|
+
module Rubygoal
|
5
|
+
class CoachDefinitionAway < CoachDefinition
|
6
|
+
|
7
|
+
team do
|
8
|
+
name "Argentina"
|
9
|
+
end
|
10
|
+
|
11
|
+
def formation(match)
|
12
|
+
formation = Formation.new
|
13
|
+
|
14
|
+
if match.me.winning?
|
15
|
+
formation.lineup do
|
16
|
+
defenders :average1, :fast1, :none, :fast3, :average5
|
17
|
+
midfielders :average2, :none, :captain, :none, :average6
|
18
|
+
att_midfielders :average3
|
19
|
+
attackers :none, :none, :fast2, :average4, :none
|
20
|
+
end
|
21
|
+
elsif match.me.draw?
|
22
|
+
formation.lineup do
|
23
|
+
lines do
|
24
|
+
defenders 13
|
25
|
+
midfielders 40
|
26
|
+
attackers 65
|
27
|
+
end
|
28
|
+
|
29
|
+
defenders :average1, :fast1, :none, :average3, :average5
|
30
|
+
midfielders :average2, :none, :none, :none, :average6
|
31
|
+
attackers :none, :fast2, :none, :average4, :none
|
32
|
+
|
33
|
+
custom_position do
|
34
|
+
player :fast3
|
35
|
+
position 30, 10
|
36
|
+
end
|
37
|
+
custom_position do
|
38
|
+
player :captain
|
39
|
+
position match.ball.x, match.ball.y
|
40
|
+
end
|
41
|
+
end
|
42
|
+
elsif match.me.losing?
|
43
|
+
if match.time < 30
|
44
|
+
formation.lineup do
|
45
|
+
defenders :none, :average2, :average4, :average5, :none
|
46
|
+
midfielders :average1, :none, :none, :fast2, :average6
|
47
|
+
attackers :none, :average3, :none, :fast3, :none
|
48
|
+
|
49
|
+
custom_position do
|
50
|
+
player :fast1
|
51
|
+
position 33, 50
|
52
|
+
end
|
53
|
+
custom_position do
|
54
|
+
player :captain
|
55
|
+
position 67, 50
|
56
|
+
end
|
57
|
+
end
|
58
|
+
else
|
59
|
+
formation.lineup do
|
60
|
+
defenders :none, :average2, :fast1, :average5, :none
|
61
|
+
def_midfielders :average4
|
62
|
+
midfielders :average1, :none, :none, :captain, :average6
|
63
|
+
att_midfielders :fast2
|
64
|
+
attackers :none, :average3, :none, :fast3, :none
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
formation
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|