UG_RRobots 1.3 → 2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/{RakeFile → Rakefile} +4 -4
- data/bin/rrobots +83 -23
- data/bin/tournament +78 -10
- data/config/locales/en.yml +17 -0
- data/config/locales/fr.yml +14 -0
- data/config/rrobots.yml +9 -2
- data/doc/install.rdoc +85 -0
- data/doc/install_fr.rdoc +86 -0
- data/doc/manual.rdoc +11 -45
- data/doc/manual_fr.rdoc +10 -45
- data/doc/usage.rdoc +56 -0
- data/doc/usage_fr.rdoc +63 -0
- data/lib/battlefield.rb +19 -6
- data/lib/bullets.rb +11 -0
- data/lib/configuration.rb +3 -1
- data/lib/misc.rb +29 -0
- data/lib/overloads.rb +25 -1
- data/lib/robot.rb +24 -2
- data/lib/robotrunner.rb +71 -3
- data/lib/tkarena.rb +17 -15
- data/robots/Killer.rb +1 -1
- data/robots/MsgBot.rb +12 -4
- data/robots/SittingDuck.rb +1 -0
- metadata +24 -4
data/lib/bullets.rb
CHANGED
@@ -35,5 +35,16 @@ class Bullet
|
|
35
35
|
@dead = true
|
36
36
|
end
|
37
37
|
end
|
38
|
+
|
39
|
+
@battlefield.mines.each do |mine|
|
40
|
+
if (Math.hypot(@y - mine.y, mine.x - @x) < 10) && (!mine.dead) && (mine.origin != origin) then
|
41
|
+
explosion = Explosion.new(@battlefield, mine.x, mine.y)
|
42
|
+
@battlefield << explosion
|
43
|
+
mine.destroy if self.energy > @battlefield.config.mines[:bullet_energy_resistance]
|
44
|
+
origin.destroyed_mines += 1
|
45
|
+
@dead = true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
38
49
|
end
|
39
50
|
end
|
data/lib/configuration.rb
CHANGED
@@ -4,6 +4,7 @@ module Configuration_accessors
|
|
4
4
|
attr_accessor :robots
|
5
5
|
attr_accessor :bullets
|
6
6
|
attr_accessor :battlefield
|
7
|
+
attr_accessor :mines
|
7
8
|
end
|
8
9
|
|
9
10
|
class Configuration
|
@@ -20,9 +21,10 @@ class Configuration
|
|
20
21
|
@filename = filename
|
21
22
|
@game = { :timeout => 50000 }
|
22
23
|
@toolboxes = { :with_toolboxes => false, :life_time => 200, :spawning_chances => 100, :energy_heal_points => 20 }
|
23
|
-
@robots = { :energy_max => 100 }
|
24
|
+
@robots = { :energy_max => 100, :nb_mines => 3, :radar_mine_scanning_performance => 500 }
|
24
25
|
@bullets = { :speed => 30 }
|
25
26
|
@battlefield = { :height => 800, :width => 800 }
|
27
|
+
@mines = { :with_mines => false, :energy_hit_points => 20, :bullet_energy_resistance => 2 }
|
26
28
|
end
|
27
29
|
|
28
30
|
end
|
data/lib/misc.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
def gem_path(path='')
|
3
|
+
# TODO : Perhaps a better way to do it ...
|
4
|
+
# regarder Rubygems pour avoir le path direct ..
|
5
|
+
table_map = Gem.location_of_caller.first.split('/')
|
6
|
+
2.times do
|
7
|
+
table_map.pop
|
8
|
+
end
|
9
|
+
table_map.push path unless path.empty?
|
10
|
+
path_prefix = table_map.join('/')
|
11
|
+
return false unless File::exist?(path_prefix)
|
12
|
+
return path_prefix
|
13
|
+
end
|
14
|
+
|
15
|
+
def init_I18n(locales)
|
16
|
+
I18n.default_locale = locales
|
17
|
+
locales_path = gem_path('config').concat("/locales")
|
18
|
+
I18n.load_path << Dir["#{locales_path}/*.yml"]
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_locale_from_env
|
22
|
+
if not ENV['LANGUAGE'].empty? then
|
23
|
+
locale = ENV['LANGUAGE'].split('_').first
|
24
|
+
elsif not ENV['LANG'].empty? then
|
25
|
+
locale = ENV['LANG'].split('_').first
|
26
|
+
else
|
27
|
+
locale = 'en'
|
28
|
+
end
|
29
|
+
end
|
data/lib/overloads.rb
CHANGED
@@ -7,4 +7,28 @@ class Numeric
|
|
7
7
|
def to_deg
|
8
8
|
self * TO_DEG
|
9
9
|
end
|
10
|
-
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module RDoc
|
13
|
+
# File lib/rdoc/usage.rb, line 98
|
14
|
+
def RDoc.file_no_exit(filename,*args)
|
15
|
+
content = IO.readlines(filename).join
|
16
|
+
markup = SM::SimpleMarkup.new
|
17
|
+
flow_convertor = SM::ToFlow.new
|
18
|
+
flow = markup.convert(content, flow_convertor)
|
19
|
+
|
20
|
+
format = "plain"
|
21
|
+
|
22
|
+
unless args.empty?
|
23
|
+
flow = extract_sections(flow, args)
|
24
|
+
end
|
25
|
+
|
26
|
+
options = RI::Options.instance
|
27
|
+
if args = ENV["RI"]
|
28
|
+
options.parse(args.split)
|
29
|
+
end
|
30
|
+
formatter = options.formatter.new(options, "")
|
31
|
+
formatter.display_flow(flow)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
data/lib/robot.rb
CHANGED
@@ -14,13 +14,22 @@ module Robot
|
|
14
14
|
define_method(n){|param| @actions[n] = param }
|
15
15
|
}
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
|
+
def self.attr_order(*names)
|
19
|
+
names.each{|n|
|
20
|
+
n = n.to_sym
|
21
|
+
define_method(n){ @orders[n] = true }
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
18
25
|
def self.attr_event(*names)
|
19
26
|
names.each{|n|
|
20
27
|
n = n.to_sym
|
21
28
|
define_method(n){ @events[n] }
|
22
29
|
}
|
23
30
|
end
|
31
|
+
|
32
|
+
|
24
33
|
|
25
34
|
#the state hash of your robot. also accessible through the attr_state methods
|
26
35
|
attr_accessor :state
|
@@ -30,6 +39,8 @@ module Robot
|
|
30
39
|
|
31
40
|
#the event hash of your robot
|
32
41
|
attr_accessor :events
|
42
|
+
|
43
|
+
attr_accessor :orders
|
33
44
|
|
34
45
|
#path to where your robot's optional skin images are
|
35
46
|
attr_accessor :skin_prefix
|
@@ -87,6 +98,8 @@ module Robot
|
|
87
98
|
|
88
99
|
#fires a bullet in the direction of your gun, power is 0.1 - 3, this power is taken from your energy
|
89
100
|
attr_action :fire
|
101
|
+
|
102
|
+
attr_order :drop_mine
|
90
103
|
|
91
104
|
#turns the robot (and the gun and the radar), max 10 degrees per tick
|
92
105
|
attr_action :turn
|
@@ -99,6 +112,9 @@ module Robot
|
|
99
112
|
|
100
113
|
#broadcast message to other robots
|
101
114
|
attr_action :broadcast
|
115
|
+
|
116
|
+
#team_broadcast message to other robots
|
117
|
+
attr_action :team_broadcast
|
102
118
|
|
103
119
|
#say something to the spectators
|
104
120
|
attr_action :say
|
@@ -109,12 +125,18 @@ module Robot
|
|
109
125
|
#distances to robots your radar swept over during last tick
|
110
126
|
attr_event :robot_scanned
|
111
127
|
|
112
|
-
#distances to
|
128
|
+
#distances to toolboxes your radar swept over during last tick
|
113
129
|
attr_event :toolbox_scanned
|
130
|
+
|
131
|
+
#distances to mines your radar swept over during last tick
|
132
|
+
attr_event :mine_scanned
|
114
133
|
|
115
134
|
#broadcasts received last turn
|
116
135
|
attr_event :broadcasts
|
117
136
|
|
137
|
+
#Team broadcasts received last turn
|
138
|
+
attr_event :team_broadcasts
|
139
|
+
|
118
140
|
end
|
119
141
|
|
120
142
|
require 'robotrunner'
|
data/lib/robotrunner.rb
CHANGED
@@ -2,7 +2,9 @@ class RobotRunner
|
|
2
2
|
|
3
3
|
STATE_IVARS = [ :x, :y, :gun_heat, :heading, :gun_heading, :radar_heading, :time, :size, :speed, :energy, :team ]
|
4
4
|
NUMERIC_ACTIONS = [ :fire, :turn, :turn_gun, :turn_radar, :accelerate ]
|
5
|
-
STRING_ACTIONS = [ :say, :broadcast ]
|
5
|
+
STRING_ACTIONS = [ :say, :broadcast, :team_broadcast ]
|
6
|
+
ORDERS = [ :drop_mine ]
|
7
|
+
|
6
8
|
|
7
9
|
STATE_IVARS.each{|iv|
|
8
10
|
attr_accessor iv
|
@@ -14,6 +16,10 @@ class RobotRunner
|
|
14
16
|
attr_accessor "#{iv}_max"
|
15
17
|
}
|
16
18
|
|
19
|
+
ORDERS.each{|iv|
|
20
|
+
attr_accessor "#{iv}_max"
|
21
|
+
}
|
22
|
+
|
17
23
|
#AI of this robot
|
18
24
|
attr_accessor :robot
|
19
25
|
|
@@ -26,10 +32,14 @@ class RobotRunner
|
|
26
32
|
#keeps track of the kills
|
27
33
|
attr_accessor :kills
|
28
34
|
|
35
|
+
attr_accessor :destroyed_mines
|
36
|
+
attr_accessor :trigged_mines
|
37
|
+
attr_accessor :dropped_mines
|
38
|
+
|
29
39
|
#keeps track of the catched toolboxes
|
30
40
|
attr_accessor :catched_toolboxes
|
31
41
|
|
32
|
-
attr_reader :actions, :speech
|
42
|
+
attr_reader :actions, :speech, :orders
|
33
43
|
|
34
44
|
def initialize robot, bf, team=0
|
35
45
|
@robot = robot
|
@@ -39,6 +49,7 @@ class RobotRunner
|
|
39
49
|
set_initial_state
|
40
50
|
@events = Hash.new{|h, k| h[k]=[]}
|
41
51
|
@actions = Hash.new(0)
|
52
|
+
@orders = Hash.new(0)
|
42
53
|
end
|
43
54
|
|
44
55
|
def skin_prefix
|
@@ -57,7 +68,10 @@ class RobotRunner
|
|
57
68
|
@energy = @battlefield.config.robots[:energy_max]
|
58
69
|
@damage_given = 0
|
59
70
|
@kills = 0
|
71
|
+
@destroyed_mines = 0
|
60
72
|
@catched_toolboxes = 0
|
73
|
+
@dropped_mines = 0
|
74
|
+
@trigged_mines = 0
|
61
75
|
teleport
|
62
76
|
end
|
63
77
|
|
@@ -79,6 +93,8 @@ class RobotRunner
|
|
79
93
|
@teleport_min, @teleport_max = 0, 100
|
80
94
|
@say_max = 256
|
81
95
|
@broadcast_max = 16
|
96
|
+
@team_broadcast_max = 16
|
97
|
+
@drop_mine_max = @battlefield.config.robots[:nb_mines]
|
82
98
|
end
|
83
99
|
|
84
100
|
def hit bullet
|
@@ -115,15 +131,28 @@ class RobotRunner
|
|
115
131
|
update_state
|
116
132
|
robot_tick
|
117
133
|
parse_actions
|
134
|
+
parse_orders
|
118
135
|
fire
|
136
|
+
drop_mine if @battlefield.with_mines
|
119
137
|
turn
|
120
138
|
move
|
121
139
|
scan
|
122
140
|
speak
|
123
141
|
broadcast
|
142
|
+
team_broadcast
|
124
143
|
@time += 1
|
125
144
|
end
|
126
145
|
|
146
|
+
def parse_orders
|
147
|
+
@orders.clear
|
148
|
+
ORDERS.each{|on|
|
149
|
+
if @robot.orders[on] = true
|
150
|
+
@orders[on] = true
|
151
|
+
end
|
152
|
+
}
|
153
|
+
@orders
|
154
|
+
end
|
155
|
+
|
127
156
|
def parse_actions
|
128
157
|
@actions.clear
|
129
158
|
NUMERIC_ACTIONS.each{|an|
|
@@ -155,7 +184,10 @@ class RobotRunner
|
|
155
184
|
@robot.send("#{k}=", v)
|
156
185
|
}
|
157
186
|
@robot.events = @events.dup
|
187
|
+
@robot.orders ||= Hash.new(0)
|
158
188
|
@robot.actions ||= Hash.new(0)
|
189
|
+
@robot.orders.clear
|
190
|
+
@robot.orders[:drop_mine] = false
|
159
191
|
@robot.actions.clear
|
160
192
|
end
|
161
193
|
|
@@ -165,7 +197,7 @@ class RobotRunner
|
|
165
197
|
end
|
166
198
|
|
167
199
|
def fire
|
168
|
-
if (@actions[:fire] > 0) && (@gun_heat == 0)
|
200
|
+
if (@actions[:fire] > 0) && (@gun_heat == 0)
|
169
201
|
bullet = Bullet.new(@battlefield, @x, @y, @gun_heading, @battlefield.config.bullets[:speed] , @actions[:fire]*3.0, self)
|
170
202
|
3.times{bullet.tick}
|
171
203
|
@battlefield << bullet
|
@@ -174,6 +206,15 @@ class RobotRunner
|
|
174
206
|
@gun_heat -= 0.1
|
175
207
|
@gun_heat = 0 if @gun_heat < 0
|
176
208
|
end
|
209
|
+
|
210
|
+
def drop_mine
|
211
|
+
if (@orders[:drop_mine] == true) && (@actions[:fire] == 0) && (@dropped_mines < @drop_mine_max) then
|
212
|
+
energy = @battlefield.config.mines[:energy_hit_points]
|
213
|
+
mine = Mine.new(@battlefield, @x, @y, energy , self)
|
214
|
+
@battlefield << mine
|
215
|
+
@dropped_mines += 1
|
216
|
+
end
|
217
|
+
end
|
177
218
|
|
178
219
|
def turn
|
179
220
|
@old_radar_heading = @radar_heading
|
@@ -222,6 +263,17 @@ class RobotRunner
|
|
222
263
|
end
|
223
264
|
end
|
224
265
|
end
|
266
|
+
@battlefield.mines.each do |mine|
|
267
|
+
if (!mine.dead)
|
268
|
+
a = Math.atan2(@y - mine.y, mine.x - @x) / Math::PI * 180 % 360
|
269
|
+
if (@old_radar_heading <= a && a <= @new_radar_heading) || (@old_radar_heading >= a && a >= @new_radar_heading) ||
|
270
|
+
(@old_radar_heading <= a+360 && a+360 <= @new_radar_heading) || (@old_radar_heading >= a+360 && a+360 >= new_radar_heading) ||
|
271
|
+
(@old_radar_heading <= a-360 && a-360 <= @new_radar_heading) || (@old_radar_heading >= a-360 && a-360 >= @new_radar_heading)
|
272
|
+
dist = Math.hypot(@y - mine.y, mine.x - @x)
|
273
|
+
@events['mine_scanned'] << [dist] if (dist < @battlefield.config.robots[:radar_mine_scanning_performance]) and (self != mine.origin)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
225
277
|
end
|
226
278
|
|
227
279
|
def speak
|
@@ -233,6 +285,22 @@ class RobotRunner
|
|
233
285
|
end
|
234
286
|
end
|
235
287
|
|
288
|
+
def team_broadcast
|
289
|
+
@battlefield.teams[self.team].each do |other|
|
290
|
+
if (other != self) && (!other.dead)
|
291
|
+
msg = other.actions[:team_broadcast]
|
292
|
+
if msg != 0
|
293
|
+
a = Math.atan2(@y - other.y, other.x - @x) / Math::PI * 180 % 360
|
294
|
+
dir = 'east'
|
295
|
+
dir = 'north' if a.between? 45,135
|
296
|
+
dir = 'west' if a.between? 135,225
|
297
|
+
dir = 'south' if a.between? 225,315
|
298
|
+
@events['team_broadcasts'] << [msg, dir]
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
236
304
|
def broadcast
|
237
305
|
@battlefield.robots.each do |other|
|
238
306
|
if (other != self) && (!other.dead)
|
data/lib/tkarena.rb
CHANGED
@@ -7,7 +7,7 @@ class TkArena
|
|
7
7
|
|
8
8
|
attr_reader :battlefield, :xres, :yres
|
9
9
|
attr_accessor :speed_multiplier, :on_game_over_handlers
|
10
|
-
attr_accessor :canvas, :boom, :robots, :bullets, :explosions, :colors, :toolboxes, :toolbox_img
|
10
|
+
attr_accessor :canvas, :boom, :robots, :bullets, :explosions, :colors, :toolboxes, :toolbox_img, :mines, :mine_img
|
11
11
|
attr_accessor :default_skin_prefix, :path_prefix
|
12
12
|
|
13
13
|
def initialize battlefield, xres, yres, speed_multiplier
|
@@ -42,14 +42,6 @@ class TkArena
|
|
42
42
|
TkPhotoImage.new(:data => Base64.encode64(data))
|
43
43
|
end
|
44
44
|
|
45
|
-
def usage
|
46
|
-
puts "usage: rrobots.rb <FirstRobotClassName[.rb]> <SecondRobotClassName[.rb]> <...>"
|
47
|
-
puts "\tthe names of the rb files have to match the class names of the robots"
|
48
|
-
puts "\t(up to 8 robots)"
|
49
|
-
puts "\te.g. 'ruby rrobots.rb SittingDuck NervousDuck'"
|
50
|
-
exit
|
51
|
-
end
|
52
|
-
|
53
45
|
def init_canvas
|
54
46
|
@canvas = TkCanvas.new(:height=>yres, :width=>xres, :scrollregion=>[0, 0, xres, yres], :background => '#000000').pack
|
55
47
|
@colors = []
|
@@ -70,10 +62,11 @@ class TkArena
|
|
70
62
|
TkPhotoImage.new(:file => "#{path_prefix}/images/explosion#{i.to_s.rjust(2, '0')}.gif")
|
71
63
|
end
|
72
64
|
@toolbox_img = TkPhotoImage.new(:file => "#{path_prefix}/images/toolbox.gif")
|
65
|
+
@mine_img = TkPhotoImage.new(:file => "#{path_prefix}/images/mine.gif")
|
73
66
|
end
|
74
67
|
|
75
68
|
def init_simulation
|
76
|
-
@robots, @bullets, @explosions, @toolboxes = {}, {}, {}, {}
|
69
|
+
@robots, @bullets, @explosions, @toolboxes, @mines = {}, {}, {}, {}, {}
|
77
70
|
TkTimer.new(20, -1, Proc.new{
|
78
71
|
begin
|
79
72
|
draw_frame
|
@@ -93,9 +86,10 @@ class TkArena
|
|
93
86
|
@explosions.reject!{|e,tko| @canvas.delete(tko) if e.dead; e.dead }
|
94
87
|
@bullets.reject!{|b,tko| @canvas.delete(tko) if b.dead; b.dead }
|
95
88
|
@toolboxes.reject!{|t,tko| @canvas.delete(tko) if t.dead; t.dead }
|
89
|
+
@mines.reject!{|m,tko| @canvas.delete(tko) if m.dead; m.dead }
|
96
90
|
@robots.reject! do |ai,tko|
|
97
91
|
if ai.dead
|
98
|
-
tko.status.configure(:text =>
|
92
|
+
tko.status.configure(:text => I18n.t('tkarena.ai_name_dead', :ai_name => ai.name.ljust(20)))
|
99
93
|
tko.each{|part| @canvas.delete(part) if part != tko.status}
|
100
94
|
true
|
101
95
|
end
|
@@ -106,17 +100,17 @@ class TkArena
|
|
106
100
|
unless @game_over
|
107
101
|
winner = @robots.keys.first
|
108
102
|
whohaswon = if winner.nil?
|
109
|
-
|
103
|
+
I18n.t('tkarena.draw')
|
110
104
|
elsif @battlefield.teams.all?{|k,t|t.size<2}
|
111
|
-
|
105
|
+
I18n.t('tkarena.no_team_won', :winner_name => winner.name)
|
112
106
|
else
|
113
|
-
|
107
|
+
I18n.t('tkarena.team_won', :winner_team => winner.team)
|
114
108
|
end
|
115
109
|
text_color = winner ? winner.team : 7
|
116
110
|
@game_over = TkcText.new(canvas,
|
117
111
|
:fill => @text_colors[text_color],
|
118
112
|
:anchor => 'c', :coords => [400,400], :font=>'courier 36', :justify => 'center',
|
119
|
-
:text => "
|
113
|
+
:text => I18n.t('tkarena.game_over').concat("\n#{whohaswon}"))
|
120
114
|
end
|
121
115
|
end
|
122
116
|
@battlefield.tick
|
@@ -125,6 +119,7 @@ class TkArena
|
|
125
119
|
|
126
120
|
def draw_battlefield
|
127
121
|
draw_toolboxes
|
122
|
+
draw_mines
|
128
123
|
draw_robots
|
129
124
|
draw_bullets
|
130
125
|
draw_explosions
|
@@ -170,6 +165,13 @@ class TkArena
|
|
170
165
|
bullet.x / 2 + 3, bullet.y / 2 + 3)
|
171
166
|
end
|
172
167
|
end
|
168
|
+
|
169
|
+
def draw_mines
|
170
|
+
@battlefield.mines.each do |mine|
|
171
|
+
@mines[mine] ||= TkcImage.new(@canvas, mine.x / 2, mine.y / 2)
|
172
|
+
@mines[mine].image(mine_img)
|
173
|
+
end
|
174
|
+
end
|
173
175
|
|
174
176
|
def draw_explosions
|
175
177
|
@battlefield.explosions.each do |explosion|
|
data/robots/Killer.rb
CHANGED
data/robots/MsgBot.rb
CHANGED
@@ -4,10 +4,18 @@ class MsgBot
|
|
4
4
|
include Robot
|
5
5
|
|
6
6
|
def tick events
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
priv = true
|
8
|
+
unless priv then
|
9
|
+
events['broadcasts'].each{|msg,dir|
|
10
|
+
say "Got message #{msg.inspect} from #{dir}!"
|
11
|
+
}
|
12
|
+
broadcast "Hello! Team #{team}!" if rand < 0.01
|
13
|
+
else
|
14
|
+
events['team_broadcasts'].each{|msg,dir|
|
15
|
+
say "Got priv message #{msg.inspect} from #{dir}!"
|
16
|
+
}
|
17
|
+
team_broadcast "Hello! Team #{team}!" if rand < 0.01
|
18
|
+
end
|
11
19
|
end
|
12
20
|
|
13
21
|
end
|