rrobots 0.0.1
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/.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
|