UG_RRobots 1.3 → 2.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.
- 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
|