rrobots 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/README.rdoc +93 -0
- data/Rakefile +1 -0
- data/VERSION +1 -0
- data/bin/rrobots +154 -0
- data/bin/tournament +275 -0
- data/lib/rrobots.rb +6 -0
- data/lib/rrobots/battlefield.rb +69 -0
- data/lib/rrobots/bullet.rb +39 -0
- data/lib/rrobots/explosion.rb +20 -0
- data/lib/rrobots/gui.rb +2 -0
- data/lib/rrobots/gui/gosuarena.rb +124 -0
- data/lib/rrobots/gui/leaderboard.rb +19 -0
- data/lib/rrobots/images/blue_body000.bmp +0 -0
- data/lib/rrobots/images/blue_radar000.bmp +0 -0
- data/lib/rrobots/images/blue_turret000.bmp +0 -0
- data/lib/rrobots/images/bullet.png +0 -0
- data/lib/rrobots/images/explosion00.bmp +0 -0
- data/lib/rrobots/images/explosion01.bmp +0 -0
- data/lib/rrobots/images/explosion02.bmp +0 -0
- data/lib/rrobots/images/explosion03.bmp +0 -0
- data/lib/rrobots/images/explosion04.bmp +0 -0
- data/lib/rrobots/images/explosion05.bmp +0 -0
- data/lib/rrobots/images/explosion06.bmp +0 -0
- data/lib/rrobots/images/explosion07.bmp +0 -0
- data/lib/rrobots/images/explosion08.bmp +0 -0
- data/lib/rrobots/images/explosion09.bmp +0 -0
- data/lib/rrobots/images/explosion10.bmp +0 -0
- data/lib/rrobots/images/explosion11.bmp +0 -0
- data/lib/rrobots/images/explosion12.bmp +0 -0
- data/lib/rrobots/images/explosion13.bmp +0 -0
- data/lib/rrobots/images/explosion14.bmp +0 -0
- data/lib/rrobots/images/lime_body000.bmp +0 -0
- data/lib/rrobots/images/lime_radar000.bmp +0 -0
- data/lib/rrobots/images/lime_turret000.bmp +0 -0
- data/lib/rrobots/images/red_body000.bmp +0 -0
- data/lib/rrobots/images/red_radar000.bmp +0 -0
- data/lib/rrobots/images/red_turret000.bmp +0 -0
- data/lib/rrobots/images/space.png +0 -0
- data/lib/rrobots/images/white_body000.bmp +0 -0
- data/lib/rrobots/images/white_radar000.bmp +0 -0
- data/lib/rrobots/images/white_turret000.bmp +0 -0
- data/lib/rrobots/images/yellow_body000.bmp +0 -0
- data/lib/rrobots/images/yellow_radar000.bmp +0 -0
- data/lib/rrobots/images/yellow_turret000.bmp +0 -0
- data/lib/rrobots/numeric.rb +10 -0
- data/lib/rrobots/robot.rb +115 -0
- data/lib/rrobots/robot_runner.rb +237 -0
- data/lib/rrobots/tournament.rb +2 -0
- data/lib/rrobots/tournament/match.rb +71 -0
- data/lib/rrobots/tournament/round.rb +56 -0
- data/lib/rrobots/version.rb +3 -0
- data/rrobots.gemspec +25 -0
- metadata +113 -0
data/lib/rrobots.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
class Battlefield
|
2
|
+
attr_reader :width
|
3
|
+
attr_reader :height
|
4
|
+
attr_reader :robots
|
5
|
+
attr_reader :teams
|
6
|
+
attr_reader :bullets
|
7
|
+
attr_reader :explosions
|
8
|
+
attr_reader :time
|
9
|
+
attr_reader :seed
|
10
|
+
attr_reader :timeout # how many ticks the match can go before ending.
|
11
|
+
attr_reader :game_over
|
12
|
+
|
13
|
+
def initialize width, height, timeout, seed
|
14
|
+
@width, @height = width, height
|
15
|
+
@seed = seed
|
16
|
+
@time = 0
|
17
|
+
@robots = []
|
18
|
+
@teams = Hash.new{|h,k| h[k] = [] }
|
19
|
+
@bullets = []
|
20
|
+
@explosions = []
|
21
|
+
@timeout = timeout
|
22
|
+
@game_over = false
|
23
|
+
srand @seed
|
24
|
+
end
|
25
|
+
|
26
|
+
def << object
|
27
|
+
case object
|
28
|
+
when RobotRunner
|
29
|
+
@robots << object
|
30
|
+
@teams[object.team] << object
|
31
|
+
when Bullet
|
32
|
+
@bullets << object
|
33
|
+
when Explosion
|
34
|
+
@explosions << object
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def tick
|
39
|
+
explosions.delete_if{|explosion| explosion.dead}
|
40
|
+
explosions.each{|explosion| explosion.tick}
|
41
|
+
|
42
|
+
bullets.delete_if{|bullet| bullet.dead}
|
43
|
+
bullets.each{|bullet| bullet.tick}
|
44
|
+
|
45
|
+
robots.each do |robot|
|
46
|
+
begin
|
47
|
+
robot.send :internal_tick unless robot.dead
|
48
|
+
rescue Exception => bang
|
49
|
+
puts "#{robot} made an exception:"
|
50
|
+
puts "#{bang.class}: #{bang}", bang.backtrace
|
51
|
+
robot.instance_eval{@energy = -1}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
@time += 1
|
56
|
+
live_robots = robots.find_all{|robot| !robot.dead}
|
57
|
+
@game_over = ( (@time >= timeout) or # timeout reached
|
58
|
+
(live_robots.length == 0) or # no robots alive, draw game
|
59
|
+
(live_robots.all?{|r| r.team == live_robots.first.team})) # all other teams are dead
|
60
|
+
not @game_over
|
61
|
+
end
|
62
|
+
|
63
|
+
def state
|
64
|
+
{:explosions => explosions.map{|e| e.state},
|
65
|
+
:bullets => bullets.map{|b| b.state},
|
66
|
+
:robots => robots.map{|r| r.state}}
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class Bullet
|
2
|
+
attr_accessor :x
|
3
|
+
attr_accessor :y
|
4
|
+
attr_accessor :heading
|
5
|
+
attr_accessor :speed
|
6
|
+
attr_accessor :energy
|
7
|
+
attr_accessor :dead
|
8
|
+
attr_accessor :origin
|
9
|
+
|
10
|
+
def initialize bf, x, y, heading, speed, energy, origin
|
11
|
+
@x, @y, @heading, @origin = x, y, heading, origin
|
12
|
+
@speed, @energy = speed, energy
|
13
|
+
@battlefield, dead = bf, false
|
14
|
+
end
|
15
|
+
|
16
|
+
def state
|
17
|
+
{:x=>x, :y=>y, :energy=>energy}
|
18
|
+
end
|
19
|
+
|
20
|
+
def tick
|
21
|
+
return if @dead
|
22
|
+
@x += Math::cos(@heading.to_rad) * @speed
|
23
|
+
@y -= Math::sin(@heading.to_rad) * @speed
|
24
|
+
|
25
|
+
@dead ||= (@x < 0) || (@x >= @battlefield.width)
|
26
|
+
@dead ||= (@y < 0) || (@y >= @battlefield.height)
|
27
|
+
|
28
|
+
@battlefield.robots.each do |other|
|
29
|
+
if (other != origin) && (Math.hypot(@y - other.y, other.x - @x) < 40) && (!other.dead)
|
30
|
+
explosion = Explosion.new(@battlefield, other.x, other.y)
|
31
|
+
@battlefield << explosion
|
32
|
+
damage = other.hit(self)
|
33
|
+
origin.damage_given += damage
|
34
|
+
origin.kills += 1 if other.dead
|
35
|
+
@dead = true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Explosion
|
2
|
+
attr_accessor :x
|
3
|
+
attr_accessor :y
|
4
|
+
attr_accessor :t
|
5
|
+
attr_accessor :dead
|
6
|
+
|
7
|
+
def initialize bf, x, y
|
8
|
+
@x, @y, @t = x, y, 0
|
9
|
+
@battlefield, dead = bf, false
|
10
|
+
end
|
11
|
+
|
12
|
+
def state
|
13
|
+
{:x=>x, :y=>y, :t=>t}
|
14
|
+
end
|
15
|
+
|
16
|
+
def tick
|
17
|
+
@t += 1
|
18
|
+
@dead ||= t > 15
|
19
|
+
end
|
20
|
+
end
|
data/lib/rrobots/gui.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'gosu'
|
2
|
+
|
3
|
+
BIG_FONT = 'Courier New'
|
4
|
+
SMALL_FONT = 'Courier New'
|
5
|
+
COLORS = ['white', 'blue', 'yellow', 'red', 'lime']
|
6
|
+
FONT_COLORS = [0xffffffff, 0xff0008ff, 0xfffff706, 0xffff0613, 0xff00ff04]
|
7
|
+
|
8
|
+
GosuRobot = Struct.new(:body, :gun, :radar, :speech, :info, :status, :color, :font_color)
|
9
|
+
|
10
|
+
module ZOrder
|
11
|
+
Background, Robot, Explosions, UI = *0..3
|
12
|
+
end
|
13
|
+
|
14
|
+
class RRobotsGameWindow < Gosu::Window
|
15
|
+
attr_reader :battlefield, :xres, :yres
|
16
|
+
attr_accessor :on_game_over_handlers, :boom, :robots, :bullets, :explosions
|
17
|
+
|
18
|
+
def initialize(battlefield, xres, yres)
|
19
|
+
super(xres, yres, false, 16)
|
20
|
+
self.caption = 'RRobots'
|
21
|
+
@font = Gosu::Font.new(self, BIG_FONT, 24)
|
22
|
+
@small_font = Gosu::Font.new(self, SMALL_FONT, 24) #xres/100
|
23
|
+
@background_image = Gosu::Image.new(self, File.join(File.dirname(__FILE__),"../images/space.png"), true)
|
24
|
+
@battlefield = battlefield
|
25
|
+
@xres, @yres = xres, yres
|
26
|
+
@on_game_over_handlers = []
|
27
|
+
init_window
|
28
|
+
init_simulation
|
29
|
+
@leaderboard = Leaderboard.new(self, @robots)
|
30
|
+
end
|
31
|
+
|
32
|
+
def on_game_over(&block)
|
33
|
+
@on_game_over_handlers << block
|
34
|
+
end
|
35
|
+
|
36
|
+
def init_window
|
37
|
+
@boom = (0..14).map do |i|
|
38
|
+
Gosu::Image.new(self, File.join(File.dirname(__FILE__),"../images/explosion#{i.to_s.rjust(2, '0')}.bmp"))
|
39
|
+
end
|
40
|
+
@bullet_image = Gosu::Image.new(self, File.join(File.dirname(__FILE__),"../images/bullet.png"))
|
41
|
+
end
|
42
|
+
|
43
|
+
def init_simulation
|
44
|
+
@robots, @bullets, @explosions = {}, {}, {}
|
45
|
+
end
|
46
|
+
|
47
|
+
def draw
|
48
|
+
simulate
|
49
|
+
draw_battlefield
|
50
|
+
@leaderboard.draw
|
51
|
+
if button_down? Gosu::Button::KbEscape
|
52
|
+
self.close
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def draw_battlefield
|
57
|
+
draw_robots
|
58
|
+
draw_bullets
|
59
|
+
draw_explosions
|
60
|
+
end
|
61
|
+
|
62
|
+
def simulate(ticks=1)
|
63
|
+
@explosions.reject!{|e,tko| e.dead }
|
64
|
+
@bullets.reject!{|b,tko| b.dead }
|
65
|
+
@robots.reject! { |ai,tko| ai.dead}
|
66
|
+
ticks.times do
|
67
|
+
if @battlefield.game_over
|
68
|
+
@on_game_over_handlers.each{|h| h.call(@battlefield) }
|
69
|
+
winner = @robots.keys.first
|
70
|
+
whohaswon = if winner.nil?
|
71
|
+
"Draw!"
|
72
|
+
elsif @battlefield.teams.all?{|k,t|t.size<2}
|
73
|
+
"#{winner.name} won!"
|
74
|
+
else
|
75
|
+
"Team #{winner.team} won!"
|
76
|
+
end
|
77
|
+
text_color = winner ? winner.team : 7
|
78
|
+
@font.draw_rel("#{whohaswon}", xres/2, yres/2, ZOrder::UI, 0.5, 0.5, 1, 1, 0xffffff00)
|
79
|
+
end
|
80
|
+
@battlefield.tick
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def draw_robots
|
85
|
+
@battlefield.robots.each_with_index do |ai, i|
|
86
|
+
next if ai.dead
|
87
|
+
col = COLORS[i % COLORS.size]
|
88
|
+
font_col = FONT_COLORS[i % FONT_COLORS.size]
|
89
|
+
@robots[ai] ||= GosuRobot.new(
|
90
|
+
Gosu::Image.new(self, File.join(File.dirname(__FILE__),"../images/#{col}_body000.bmp")),
|
91
|
+
Gosu::Image.new(self, File.join(File.dirname(__FILE__),"../images/#{col}_turret000.bmp")),
|
92
|
+
Gosu::Image.new(self, File.join(File.dirname(__FILE__),"../images/#{col}_radar000.bmp")),
|
93
|
+
@small_font,
|
94
|
+
@small_font,
|
95
|
+
@small_font,
|
96
|
+
col,
|
97
|
+
font_col
|
98
|
+
)
|
99
|
+
@robots[ai].body.draw_rot(ai.x / 2, ai.y / 2, ZOrder::Robot, (-(ai.heading-90)) % 360)
|
100
|
+
@robots[ai].gun.draw_rot(ai.x / 2, ai.y / 2, ZOrder::Robot, (-(ai.gun_heading-90)) % 360)
|
101
|
+
@robots[ai].radar.draw_rot(ai.x / 2, ai.y / 2, ZOrder::Robot, (-(ai.radar_heading-90)) % 360)
|
102
|
+
|
103
|
+
@robots[ai].speech.draw_rel(ai.speech.to_s, ai.x / 2, ai.y / 2 - 40, ZOrder::UI, 0.5, 0.5, 1, 1, font_col)
|
104
|
+
@robots[ai].info.draw_rel("#{ai.name}", ai.x / 2, ai.y / 2 + 30, ZOrder::UI, 0.5, 0.5, 1, 1, font_col)
|
105
|
+
@robots[ai].info.draw_rel("#{ai.energy.to_i}", ai.x / 2, ai.y / 2 + 50, ZOrder::UI, 0.5, 0.5, 1, 1, font_col)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def draw_bullets
|
110
|
+
@battlefield.bullets.each do |bullet|
|
111
|
+
@bullets[bullet] ||= @bullet_image
|
112
|
+
@bullets[bullet].draw(bullet.x / 2, bullet.y / 2, ZOrder::Explosions)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def draw_explosions
|
117
|
+
@battlefield.explosions.each do |explosion|
|
118
|
+
@explosions[explosion] = boom[explosion.t % 14]
|
119
|
+
@explosions[explosion].draw_rot(explosion.x / 2, explosion.y / 2, ZOrder::Explosions, 0)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Leaderboard
|
2
|
+
def initialize(window, robots)
|
3
|
+
@font_size = 24
|
4
|
+
@robots = robots
|
5
|
+
@font = Gosu::Font.new(window, 'Courier New', @font_size)
|
6
|
+
@x_offset = @font_size
|
7
|
+
@y_offset = @font_size * 2
|
8
|
+
end
|
9
|
+
|
10
|
+
def draw
|
11
|
+
if @robots
|
12
|
+
@robots.each_with_index do |r, i|
|
13
|
+
y = @y_offset + i * @font_size
|
14
|
+
@font.draw("#{r.first.name}", @x_offset, y, ZOrder::UI, 1.0, 1.0, r.last.font_color)
|
15
|
+
@font.draw("#{r.first.energy.to_i}", @x_offset + (@font_size * 6), y, ZOrder::UI, 1.0, 1.0, r.last.font_color)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module Robot
|
2
|
+
|
3
|
+
def self.attr_state(*names)
|
4
|
+
names.each{|n|
|
5
|
+
n = n.to_sym
|
6
|
+
attr_writer n
|
7
|
+
attr_reader n
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.attr_action(*names)
|
12
|
+
names.each{|n|
|
13
|
+
n = n.to_sym
|
14
|
+
define_method(n){|param| @actions[n] = param }
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.attr_event(*names)
|
19
|
+
names.each{|n|
|
20
|
+
n = n.to_sym
|
21
|
+
define_method(n){ @events[n] }
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
#the state hash of your robot. also accessible through the attr_state methods
|
26
|
+
attr_accessor :state
|
27
|
+
|
28
|
+
#the action hash of your robot
|
29
|
+
attr_accessor :actions
|
30
|
+
|
31
|
+
#the event hash of your robot
|
32
|
+
attr_accessor :events
|
33
|
+
|
34
|
+
#path to where your robot's optional skin images are
|
35
|
+
attr_accessor :skin_prefix
|
36
|
+
|
37
|
+
#team of your robot
|
38
|
+
attr_state :team
|
39
|
+
|
40
|
+
#the height of the battlefield
|
41
|
+
attr_state :battlefield_height
|
42
|
+
|
43
|
+
#the width of the battlefield
|
44
|
+
attr_state :battlefield_width
|
45
|
+
|
46
|
+
#your remaining energy (if this drops below 0 you are dead)
|
47
|
+
attr_state :energy
|
48
|
+
|
49
|
+
#the heading of your gun, 0 pointing east, 90 pointing north, 180 pointing west, 270 pointing south
|
50
|
+
attr_state :gun_heading
|
51
|
+
|
52
|
+
#your gun heat, if this is above 0 you can't shoot
|
53
|
+
attr_state :gun_heat
|
54
|
+
|
55
|
+
#your robots heading, 0 pointing east, 90 pointing north, 180 pointing west, 270 pointing south
|
56
|
+
attr_state :heading
|
57
|
+
|
58
|
+
#your robots radius, if x <= size you hit the left wall
|
59
|
+
attr_state :size
|
60
|
+
|
61
|
+
#the heading of your radar, 0 pointing east, 90 pointing north, 180 pointing west, 270 pointing south
|
62
|
+
attr_state :radar_heading
|
63
|
+
|
64
|
+
#ticks since match start
|
65
|
+
attr_state :time
|
66
|
+
|
67
|
+
#whether the match is over or not, remember to go into cheer mode when this is true ;)
|
68
|
+
attr_state :game_over
|
69
|
+
|
70
|
+
#your speed (-8..8)
|
71
|
+
attr_state :speed
|
72
|
+
alias :velocity :speed
|
73
|
+
|
74
|
+
#your x coordinate, 0...battlefield_width
|
75
|
+
attr_state :x
|
76
|
+
|
77
|
+
#your y coordinate, 0...battlefield_height
|
78
|
+
attr_state :y
|
79
|
+
|
80
|
+
#accelerate (max speed is 8, max accelerate is 1/-1, negativ speed means moving backwards)
|
81
|
+
attr_action :accelerate
|
82
|
+
|
83
|
+
#accelerates negativ if moving forward (and vice versa), may take 8 ticks to stop (and you have to call it every tick)
|
84
|
+
def stop
|
85
|
+
accelerate((speed > 0) ? -1 : ((speed < 0) ? 1 :0))
|
86
|
+
end
|
87
|
+
|
88
|
+
#fires a bullet in the direction of your gun, power is 0.1 - 3, this power is taken from your energy
|
89
|
+
attr_action :fire
|
90
|
+
|
91
|
+
#turns the robot (and the gun and the radar), max 10 degrees per tick
|
92
|
+
attr_action :turn
|
93
|
+
|
94
|
+
#turns the gun (and the radar), max 30 degrees per tick
|
95
|
+
attr_action :turn_gun
|
96
|
+
|
97
|
+
#turns the radar, max 60 degrees per tick
|
98
|
+
attr_action :turn_radar
|
99
|
+
|
100
|
+
#broadcast message to other robots
|
101
|
+
attr_action :broadcast
|
102
|
+
|
103
|
+
#say something to the spectators
|
104
|
+
attr_action :say
|
105
|
+
|
106
|
+
#if you got hit last turn, this won't be empty
|
107
|
+
attr_event :got_hit
|
108
|
+
|
109
|
+
#distances to robots your radar swept over during last tick
|
110
|
+
attr_event :robot_scanned
|
111
|
+
|
112
|
+
#broadcasts received last turn
|
113
|
+
attr_event :broadcasts
|
114
|
+
|
115
|
+
end
|