rubots 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/bin/rubots +5 -0
- data/lib/rubots.rb +17 -0
- data/lib/rubots/beam.rb +48 -0
- data/lib/rubots/command.rb +6 -0
- data/lib/rubots/command/base.rb +10 -0
- data/lib/rubots/command/do_nothing.rb +10 -0
- data/lib/rubots/command/fire.rb +13 -0
- data/lib/rubots/command/rotate_gun_to.rb +14 -0
- data/lib/rubots/command/rotate_to.rb +13 -0
- data/lib/rubots/command/throttle.rb +19 -0
- data/lib/rubots/game.rb +81 -0
- data/lib/rubots/graphics.rb +4 -0
- data/lib/rubots/graphics/assets.rb +11 -0
- data/lib/rubots/graphics/beam.rb +32 -0
- data/lib/rubots/graphics/robot.rb +25 -0
- data/lib/rubots/graphics/window.rb +43 -0
- data/lib/rubots/robot.rb +152 -0
- data/lib/rubots/samples.rb +4 -0
- data/lib/rubots/samples/artillery.rb +24 -0
- data/lib/rubots/samples/rotator.rb +24 -0
- data/lib/rubots/samples/sitting_duck.rb +11 -0
- data/lib/rubots/samples/target_finder.rb +68 -0
- data/lib/rubots/strategy.rb +44 -0
- data/lib/rubots/strategy_loader.rb +51 -0
- data/media/deadbot.png +0 -0
- data/media/gun.png +0 -0
- data/media/pew.wav +0 -0
- data/media/robot.png +0 -0
- metadata +91 -0
data/bin/rubots
ADDED
data/lib/rubots.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'gosu'
|
2
|
+
require 'rubots/graphics'
|
3
|
+
require 'rubots/game'
|
4
|
+
require 'rubots/robot'
|
5
|
+
require 'rubots/command'
|
6
|
+
require 'rubots/beam'
|
7
|
+
require 'rubots/strategy'
|
8
|
+
require 'rubots/strategy_loader'
|
9
|
+
require 'rubots/samples'
|
10
|
+
|
11
|
+
module Rubots
|
12
|
+
def self.run_game(params)
|
13
|
+
game = Game.new StrategyLoader.load(params)
|
14
|
+
|
15
|
+
Graphics::Window.new(game).show
|
16
|
+
end
|
17
|
+
end
|
data/lib/rubots/beam.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
module Rubots
|
2
|
+
class Beam
|
3
|
+
attr_reader :source_x, :source_y, :angle
|
4
|
+
|
5
|
+
def initialize(source_x, source_y, angle)
|
6
|
+
@source_x = source_x
|
7
|
+
@source_y = source_y
|
8
|
+
@angle = angle
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.from(robot)
|
12
|
+
real_angle = (robot.angle + robot.gun_angle) % 360
|
13
|
+
new robot.x, robot.y, real_angle
|
14
|
+
end
|
15
|
+
|
16
|
+
def check_hit(robot)
|
17
|
+
if found_hit?(robot)
|
18
|
+
robot.destroy
|
19
|
+
puts "#{robot.name} hit by laser"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Laser beam is:
|
24
|
+
# (x - x0) = tan(angle)(y0 - y)
|
25
|
+
# We check for X:
|
26
|
+
# x = tan(angle)(y0 - y) + x0
|
27
|
+
# We check for Y
|
28
|
+
# y = (x0 - x)/tan(angle) - y0
|
29
|
+
HIT_THRESHOLD = 16
|
30
|
+
def found_hit?(robot)
|
31
|
+
return false if robot.x == @source_x && robot.y == @source_y # Avoid hitting self
|
32
|
+
|
33
|
+
# Check we're pointing to the right quadrant
|
34
|
+
return false if robot.x > @source_x && (@angle > 190 && @angle < 350)
|
35
|
+
return false if robot.x < @source_x && (@angle < 170 && @angle > 10)
|
36
|
+
return false if robot.y > @source_y && (@angle > 280 || @angle < 80)
|
37
|
+
return false if robot.y < @source_y && (@angle < 260 && @angle > 100)
|
38
|
+
|
39
|
+
tan_angle = Math.tan(@angle * Math::PI / 180)
|
40
|
+
|
41
|
+
test_x = tan_angle * (@source_y - robot.y) + @source_x
|
42
|
+
return true if (test_x - robot.x).abs < HIT_THRESHOLD
|
43
|
+
|
44
|
+
test_y = (@source_x - robot.x)/tan_angle - @source_y
|
45
|
+
(test_y - robot.y).abs < HIT_THRESHOLD
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Rubots
|
2
|
+
module Command
|
3
|
+
class Throttle
|
4
|
+
def initialize(throttle)
|
5
|
+
@throttle = throttle
|
6
|
+
@throttle = 0 if @throttle < 0
|
7
|
+
@throttle = Robot::MAX_THROTTLE if @throttle > Robot::MAX_THROTTLE
|
8
|
+
end
|
9
|
+
|
10
|
+
def apply_to(robot)
|
11
|
+
robot.desired_throttle = @throttle
|
12
|
+
end
|
13
|
+
|
14
|
+
def cooldown
|
15
|
+
5
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/rubots/game.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
module Rubots
|
2
|
+
class Game
|
3
|
+
attr_reader :robots, :laser_beams
|
4
|
+
MAP_HEIGHT = 700
|
5
|
+
MAP_WIDTH = 1000
|
6
|
+
|
7
|
+
def initialize(robots)
|
8
|
+
@robots = robots.map { |klass| Robot.new(klass, self, *random_location) }
|
9
|
+
end
|
10
|
+
|
11
|
+
def tick
|
12
|
+
@laser_beams = []
|
13
|
+
@robots.each { |robot| robot.tick }
|
14
|
+
@robots.each { |robot| robot.tick_fire }
|
15
|
+
check_collisions
|
16
|
+
check_out_of_area
|
17
|
+
check_beam_hits
|
18
|
+
clean_up_bodies
|
19
|
+
end
|
20
|
+
|
21
|
+
def map
|
22
|
+
OpenStruct.new width: MAP_WIDTH, height: MAP_HEIGHT
|
23
|
+
end
|
24
|
+
|
25
|
+
def laser_fire(beam)
|
26
|
+
@laser_beams << beam
|
27
|
+
end
|
28
|
+
|
29
|
+
def over?
|
30
|
+
@robots.count <= 1
|
31
|
+
end
|
32
|
+
|
33
|
+
def winner
|
34
|
+
return nil unless over?
|
35
|
+
|
36
|
+
@robots.first || OpenStruct.new(name: "Nobody")
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# TODO enforce separation
|
42
|
+
def random_location
|
43
|
+
x = rand(MAP_WIDTH)
|
44
|
+
y = rand(MAP_HEIGHT)
|
45
|
+
[x, y]
|
46
|
+
end
|
47
|
+
|
48
|
+
COLLISION_DISTANCE = 32
|
49
|
+
def check_collisions
|
50
|
+
@robots.each do |r1|
|
51
|
+
@robots.each do |r2|
|
52
|
+
next if r1 == r2
|
53
|
+
if r1.distance_to(r2) < COLLISION_DISTANCE
|
54
|
+
r1.destroy
|
55
|
+
puts "#{r1.name} destroyed by collision with #{r2.name}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def check_beam_hits
|
62
|
+
@laser_beams.each do |beam|
|
63
|
+
@robots.each { |robot| beam.check_hit robot }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def check_out_of_area
|
68
|
+
@robots.each do |r|
|
69
|
+
if r.x > MAP_WIDTH || r.y > MAP_HEIGHT || r.x < 0 || r.y < 0
|
70
|
+
r.destroy
|
71
|
+
puts "#{r.name} hit the invisible wall and was destroyed"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def clean_up_bodies
|
77
|
+
@robots.reject!(&:destroyed?)
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Rubots
|
2
|
+
module Graphics
|
3
|
+
class Assets
|
4
|
+
MEDIA_DIR = File.join(File.dirname(__FILE__), '..', '..', '..', 'media')
|
5
|
+
PEW_SOUND = File.join(MEDIA_DIR, "pew.wav")
|
6
|
+
ROBOT_IMG = File.join(MEDIA_DIR, "robot.png")
|
7
|
+
GUN_IMG = File.join(MEDIA_DIR, "gun.png")
|
8
|
+
DEAD_IMG = File.join(MEDIA_DIR, "deadbot.png")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Rubots
|
2
|
+
module Graphics
|
3
|
+
class Beam
|
4
|
+
BEAM_LENGTH = 10000 # As long as it goes out of the screen it's good
|
5
|
+
BEAM_DURATION = 30 # For how many ticks is the beam visible
|
6
|
+
|
7
|
+
def initialize(window, beam)
|
8
|
+
@beam = beam
|
9
|
+
@duration = BEAM_DURATION
|
10
|
+
@window = window
|
11
|
+
@pew = Gosu::Sample.new(window, Assets::PEW_SOUND)
|
12
|
+
@pew.play
|
13
|
+
end
|
14
|
+
|
15
|
+
def decay
|
16
|
+
@duration -= 1
|
17
|
+
end
|
18
|
+
|
19
|
+
def expired?
|
20
|
+
@duration <= 0
|
21
|
+
end
|
22
|
+
|
23
|
+
def draw
|
24
|
+
rad_angle = @beam.angle * Math::PI / 180
|
25
|
+
x_end = @beam.source_x + Math.sin(rad_angle) * BEAM_LENGTH
|
26
|
+
y_end = @beam.source_y + Math.cos(rad_angle) * BEAM_LENGTH * -1
|
27
|
+
beam_color = 0xffff0000
|
28
|
+
@window.draw_line @beam.source_x, @beam.source_y, beam_color, x_end, y_end, beam_color
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Rubots
|
2
|
+
module Graphics
|
3
|
+
class Robot
|
4
|
+
Z_AXIS = 10
|
5
|
+
|
6
|
+
def initialize(window, robot)
|
7
|
+
@robot = robot
|
8
|
+
@image = Gosu::Image.new(window, Assets::ROBOT_IMG, false)
|
9
|
+
@gun_image = Gosu::Image.new(window, Assets::GUN_IMG, false)
|
10
|
+
@dead_image = Gosu::Image.new(window, Assets::DEAD_IMG, false)
|
11
|
+
@font = Gosu::Font.new(window, Gosu::default_font_name, 14)
|
12
|
+
end
|
13
|
+
|
14
|
+
def draw
|
15
|
+
if @robot.destroyed?
|
16
|
+
@dead_image.draw_rot(@robot.x, @robot.y, Z_AXIS, 0)
|
17
|
+
else
|
18
|
+
@image.draw_rot(@robot.x, @robot.y, Z_AXIS, @robot.angle)
|
19
|
+
@gun_image.draw_rot(@robot.x, @robot.y, Z_AXIS + 1, @robot.angle + @robot.gun_angle)
|
20
|
+
end
|
21
|
+
@font.draw_rel(@robot.name, @robot.x, @robot.y + 20, Z_AXIS + 2, 0.5, 1.0, 1.0, 1.0, 0xffffff00)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Rubots
|
2
|
+
module Graphics
|
3
|
+
class Window < Gosu::Window
|
4
|
+
def initialize(game)
|
5
|
+
@game = game
|
6
|
+
super Game::MAP_WIDTH, Game::MAP_HEIGHT, false
|
7
|
+
self.caption = "LASER RUBOTS PEW PEW PEW"
|
8
|
+
@robots = @game.robots.map { |r| Robot.new self, r }
|
9
|
+
@beams = []
|
10
|
+
end
|
11
|
+
|
12
|
+
GAME_END_TICKS = 150
|
13
|
+
def update
|
14
|
+
if !@game_over
|
15
|
+
@game.tick
|
16
|
+
@beams += @game.laser_beams.map { |b| Beam.new self, b }
|
17
|
+
decay_beams
|
18
|
+
@game_over = @game.over?
|
19
|
+
else
|
20
|
+
@game_over_countdown ||= GAME_END_TICKS
|
21
|
+
@game_over_countdown -= 1
|
22
|
+
if @game_over_countdown == 0
|
23
|
+
puts "#{@game.winner.name} wins the game."
|
24
|
+
exit
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
def draw
|
31
|
+
@robots.each &:draw
|
32
|
+
@beams.each &:draw
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def decay_beams
|
38
|
+
@beams.each(&:decay)
|
39
|
+
@beams.reject!(&:expired?)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/rubots/robot.rb
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module Rubots
|
4
|
+
class Robot
|
5
|
+
attr_reader :x, :y, :angle, :gun_angle
|
6
|
+
attr_writer :desired_angle, :desired_throttle, :desired_gun_angle
|
7
|
+
|
8
|
+
# Some constants
|
9
|
+
MAX_ANGLE = 360 # Maximum angle. Wraps around to zero after this.
|
10
|
+
ANGLE_STEP = 1 # How much we change angle each tick.
|
11
|
+
|
12
|
+
MAX_THROTTLE = 10 # Max throttle. Can't go above.
|
13
|
+
THROTTLE_STEP = 1 # How much we change throttle each tick.
|
14
|
+
|
15
|
+
SPEED_FACTOR = 1 # How much movement does each throttle step represent
|
16
|
+
|
17
|
+
MAX_GUN_ANGLE = 360 # Maximum gun angle. Wraps around to zero after this.
|
18
|
+
GUN_ANGLE_STEP = 2 # How much we change angle each tick.
|
19
|
+
|
20
|
+
|
21
|
+
def initialize(strategy_class, game, x, y)
|
22
|
+
@x = x
|
23
|
+
@y = y
|
24
|
+
@throttle = @desired_throttle = 0.0
|
25
|
+
@angle = @desired_angle = 0.0
|
26
|
+
@gun_angle = @desired_gun_angle = 0.0
|
27
|
+
@game = game
|
28
|
+
|
29
|
+
@strategy = strategy_class.new(@game.map, robot_data, nil)
|
30
|
+
|
31
|
+
@destroyed = false
|
32
|
+
|
33
|
+
@cooldown_timer = 0
|
34
|
+
end
|
35
|
+
|
36
|
+
def tick
|
37
|
+
return if @destroyed
|
38
|
+
|
39
|
+
if @cooldown_timer > 0
|
40
|
+
@cooldown_timer -= 1
|
41
|
+
else
|
42
|
+
command = @strategy.get_command(robot_data, targets_data)
|
43
|
+
command.apply_to(self)
|
44
|
+
@cooldown_timer = command.cooldown
|
45
|
+
end
|
46
|
+
tick_angle
|
47
|
+
tick_throttle
|
48
|
+
tick_movement
|
49
|
+
tick_gun
|
50
|
+
end
|
51
|
+
|
52
|
+
# It's a separate method because we want fire to be after *every* robot moved
|
53
|
+
def tick_fire
|
54
|
+
if @firing
|
55
|
+
@game.laser_fire(Beam.from(self))
|
56
|
+
@firing = false
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def name
|
61
|
+
@strategy.name
|
62
|
+
end
|
63
|
+
|
64
|
+
def do_fire
|
65
|
+
@firing = true
|
66
|
+
end
|
67
|
+
|
68
|
+
def distance_to(other)
|
69
|
+
x_dist = @x - other.x
|
70
|
+
y_dist = @y - other.y
|
71
|
+
Math.sqrt(x_dist ** 2 + y_dist ** 2)
|
72
|
+
end
|
73
|
+
|
74
|
+
def destroy
|
75
|
+
@throttle = 0
|
76
|
+
@destroyed = true
|
77
|
+
end
|
78
|
+
|
79
|
+
def destroyed?
|
80
|
+
@destroyed
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def robot_data
|
86
|
+
OpenStruct.new x: @x, y: @y, angle: @angle, throttle: @throttle, gun_angle: @gun_angle
|
87
|
+
end
|
88
|
+
|
89
|
+
def targets_data
|
90
|
+
@game.robots.map do |target_robot|
|
91
|
+
next if target_robot == self
|
92
|
+
x_dist = @x - target_robot.x
|
93
|
+
y_dist = @y - target_robot.y
|
94
|
+
distance = Math.sqrt(x_dist ** 2 + y_dist ** 2)
|
95
|
+
actual_angle = Math.atan2(x_dist, -y_dist) * 180 / Math::PI + 180
|
96
|
+
relative_angle = (actual_angle - @angle) % 360
|
97
|
+
|
98
|
+
OpenStruct.new name: target_robot.name, distance: distance, angle: relative_angle
|
99
|
+
end.compact
|
100
|
+
end
|
101
|
+
|
102
|
+
def tick_angle
|
103
|
+
if @desired_angle != @angle
|
104
|
+
if (@desired_angle - @angle).abs < ANGLE_STEP
|
105
|
+
@angle = @desired_angle # Fractional angles
|
106
|
+
else
|
107
|
+
diff = @desired_angle - @angle
|
108
|
+
sign = diff / diff.abs
|
109
|
+
sign = sign * -1 if diff.abs > MAX_ANGLE / 2.0
|
110
|
+
@angle += sign * ANGLE_STEP
|
111
|
+
@angle += MAX_ANGLE if @angle < 0
|
112
|
+
@angle -= MAX_ANGLE if @angle >= MAX_ANGLE
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def tick_throttle
|
118
|
+
if @desired_throttle != @throttle
|
119
|
+
diff = @desired_throttle - @throttle
|
120
|
+
if diff.abs < THROTTLE_STEP
|
121
|
+
@throttle = @desired_throttle
|
122
|
+
else
|
123
|
+
@throttle += (diff / diff.abs) * THROTTLE_STEP
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def tick_movement
|
129
|
+
movement = @throttle * SPEED_FACTOR
|
130
|
+
rad_angle = angle * Math::PI / 180
|
131
|
+
mov_x = Math.sin(rad_angle) * movement
|
132
|
+
mov_y = Math.cos(rad_angle) * movement * -1
|
133
|
+
@x += mov_x
|
134
|
+
@y += mov_y
|
135
|
+
end
|
136
|
+
|
137
|
+
def tick_gun
|
138
|
+
if @desired_gun_angle != @gun_angle
|
139
|
+
if (@desired_gun_angle - @gun_angle).abs < GUN_ANGLE_STEP
|
140
|
+
@gun_angle = @desired_gun_angle # Fractional angles
|
141
|
+
else
|
142
|
+
diff = @desired_gun_angle - @gun_angle
|
143
|
+
sign = diff / diff.abs
|
144
|
+
sign = sign * -1 if diff.abs > MAX_GUN_ANGLE / 2.0
|
145
|
+
@gun_angle += sign * GUN_ANGLE_STEP
|
146
|
+
@gun_angle += MAX_GUN_ANGLE if @gun_angle < 0
|
147
|
+
@gun_angle -= MAX_GUN_ANGLE if @gun_angle >= MAX_GUN_ANGLE
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Rubots
|
2
|
+
module Samples
|
3
|
+
# Points at a target and fires a single shot.
|
4
|
+
class Artillery < Strategy
|
5
|
+
def initialize(map, me, targets)
|
6
|
+
@fired = false
|
7
|
+
end
|
8
|
+
|
9
|
+
def command(me, targets)
|
10
|
+
if me.angle != 90
|
11
|
+
rotate_to 90
|
12
|
+
elsif me.gun_angle != targets.first.angle
|
13
|
+
rotate_gun_to targets.first.angle
|
14
|
+
else
|
15
|
+
fire
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def name
|
20
|
+
"Artillery"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Rubots
|
2
|
+
module Samples
|
3
|
+
# Rotates in place direction
|
4
|
+
class Rotator < Strategy
|
5
|
+
def initialize(map, me, targets)
|
6
|
+
@direction = :left
|
7
|
+
end
|
8
|
+
|
9
|
+
def command(me, targets)
|
10
|
+
if @direction == :left
|
11
|
+
@direction = :right if me.angle == 270
|
12
|
+
rotate_to 270
|
13
|
+
else
|
14
|
+
@direction = :left if me.angle == 89
|
15
|
+
rotate_to 89
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def name
|
20
|
+
"Rotator"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Rubots
|
2
|
+
module Samples
|
3
|
+
# Homes into the center of the map, then stays there, points up, and aims down.
|
4
|
+
class TargetFinder < Strategy
|
5
|
+
ANGLE_DOWN = 180
|
6
|
+
ANGLE_UP = 0
|
7
|
+
ANGLE_LEFT = 270
|
8
|
+
ANGLE_RIGHT = 90
|
9
|
+
|
10
|
+
def initialize(map, me, targets)
|
11
|
+
@finding = :x
|
12
|
+
@map = map
|
13
|
+
@find_x = map.width / 2
|
14
|
+
@find_y = map.height / 2
|
15
|
+
end
|
16
|
+
|
17
|
+
def command(me, targets)
|
18
|
+
if @finding == :x
|
19
|
+
find_x(me)
|
20
|
+
elsif @finding == :y
|
21
|
+
find_y(me)
|
22
|
+
elsif me.angle != 0
|
23
|
+
rotate_to 0
|
24
|
+
else
|
25
|
+
rotate_gun_to 180
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def name
|
30
|
+
"GoToCenter"
|
31
|
+
end
|
32
|
+
|
33
|
+
def find_x(me)
|
34
|
+
if me.x > @find_x && me.angle != ANGLE_LEFT
|
35
|
+
rotate_to ANGLE_LEFT
|
36
|
+
elsif me.x < @find_x && me.angle != ANGLE_RIGHT
|
37
|
+
rotate_to ANGLE_RIGHT
|
38
|
+
elsif me.x != @find_x
|
39
|
+
if (@find_x - me.x).abs > 50
|
40
|
+
throttle 4
|
41
|
+
else
|
42
|
+
throttle 1
|
43
|
+
end
|
44
|
+
else # At pos x
|
45
|
+
@finding = :y
|
46
|
+
throttle 0
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def find_y(me)
|
51
|
+
if me.y > @find_y && me.angle != ANGLE_UP
|
52
|
+
rotate_to ANGLE_UP
|
53
|
+
elsif me.y < @find_y && me.angle != ANGLE_DOWN
|
54
|
+
rotate_to ANGLE_DOWN
|
55
|
+
elsif me.y != @find_y
|
56
|
+
if (@find_y - me.y).abs > 50
|
57
|
+
throttle 5
|
58
|
+
else
|
59
|
+
throttle 1
|
60
|
+
end
|
61
|
+
else # At pos y
|
62
|
+
@finding = :none
|
63
|
+
throttle 0
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Rubots
|
2
|
+
class Strategy
|
3
|
+
def initialize(map, me, targets)
|
4
|
+
end
|
5
|
+
|
6
|
+
# Called by the Robot to get a command
|
7
|
+
def get_command(me, targets)
|
8
|
+
@command_queue ||= []
|
9
|
+
command(me, targets) if @command_queue.empty?
|
10
|
+
@command_queue.shift || Command::DoNothing.new
|
11
|
+
end
|
12
|
+
|
13
|
+
# When get_command is out of commands, it calls this, which will queue commands
|
14
|
+
def command(me, targets)
|
15
|
+
# Implement in subclass
|
16
|
+
end
|
17
|
+
|
18
|
+
def name
|
19
|
+
"Unnamed robot"
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def rotate_to(angle)
|
25
|
+
@command_queue << Command::RotateTo.new(angle)
|
26
|
+
end
|
27
|
+
|
28
|
+
def rotate_gun_to(angle)
|
29
|
+
@command_queue << Command::RotateGunTo.new(angle)
|
30
|
+
end
|
31
|
+
|
32
|
+
def throttle(throttle)
|
33
|
+
@command_queue << Command::Throttle.new(throttle)
|
34
|
+
end
|
35
|
+
|
36
|
+
def fire
|
37
|
+
@command_queue << Command::Fire.new
|
38
|
+
end
|
39
|
+
|
40
|
+
def do_nothing
|
41
|
+
@command_queue << Command::DoNothing.new
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Rubots
|
2
|
+
class StrategyLoader
|
3
|
+
def self.load(params)
|
4
|
+
return default_lineup unless params.any?
|
5
|
+
|
6
|
+
params.map { |p| new(p).strategy_class }
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.default_lineup
|
10
|
+
[ Samples::Rotator, Samples::SittingDuck, Samples::TargetFinder,
|
11
|
+
Samples::Artillery ]
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(name)
|
15
|
+
@name = name
|
16
|
+
end
|
17
|
+
|
18
|
+
def strategy_class
|
19
|
+
if is_sample?
|
20
|
+
sample_class
|
21
|
+
else
|
22
|
+
class_from_file
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def class_from_file
|
29
|
+
load @name
|
30
|
+
|
31
|
+
class_name = camelize(File.basename(@name, ".rb"))
|
32
|
+
Rubots.const_get(class_name)
|
33
|
+
end
|
34
|
+
|
35
|
+
def is_sample?
|
36
|
+
@name.match /^sample:/
|
37
|
+
end
|
38
|
+
|
39
|
+
def sample_class
|
40
|
+
Samples.const_get(camelize(@name.split(':').last))
|
41
|
+
end
|
42
|
+
|
43
|
+
def camelize(term)
|
44
|
+
string = term.to_s
|
45
|
+
string = string.sub(/^[a-z\d]*/) { $&.capitalize }
|
46
|
+
string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }
|
47
|
+
string.gsub!('/', '::')
|
48
|
+
string
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/media/deadbot.png
ADDED
Binary file
|
data/media/gun.png
ADDED
Binary file
|
data/media/pew.wav
ADDED
Binary file
|
data/media/robot.png
ADDED
Binary file
|
metadata
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rubots
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Nacho Facello
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-06-04 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: gosu
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.7.50
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.7.50
|
30
|
+
description: A small simple game where you program robots to fight to the death using
|
31
|
+
red lasers
|
32
|
+
email: nacho@nucleartesuji.com
|
33
|
+
executables:
|
34
|
+
- rubots
|
35
|
+
extensions: []
|
36
|
+
extra_rdoc_files: []
|
37
|
+
files:
|
38
|
+
- lib/rubots/samples.rb
|
39
|
+
- lib/rubots/graphics.rb
|
40
|
+
- lib/rubots/game.rb
|
41
|
+
- lib/rubots/beam.rb
|
42
|
+
- lib/rubots/command/rotate_gun_to.rb
|
43
|
+
- lib/rubots/command/throttle.rb
|
44
|
+
- lib/rubots/command/do_nothing.rb
|
45
|
+
- lib/rubots/command/rotate_to.rb
|
46
|
+
- lib/rubots/command/fire.rb
|
47
|
+
- lib/rubots/command/base.rb
|
48
|
+
- lib/rubots/strategy.rb
|
49
|
+
- lib/rubots/strategy_loader.rb
|
50
|
+
- lib/rubots/robot.rb
|
51
|
+
- lib/rubots/graphics/assets.rb
|
52
|
+
- lib/rubots/graphics/beam.rb
|
53
|
+
- lib/rubots/graphics/robot.rb
|
54
|
+
- lib/rubots/graphics/window.rb
|
55
|
+
- lib/rubots/samples/sitting_duck.rb
|
56
|
+
- lib/rubots/samples/rotator.rb
|
57
|
+
- lib/rubots/samples/artillery.rb
|
58
|
+
- lib/rubots/samples/target_finder.rb
|
59
|
+
- lib/rubots/command.rb
|
60
|
+
- lib/rubots.rb
|
61
|
+
- media/deadbot.png
|
62
|
+
- media/gun.png
|
63
|
+
- media/robot.png
|
64
|
+
- media/pew.wav
|
65
|
+
- bin/rubots
|
66
|
+
homepage: http://github.com/nachof/laser_rubots
|
67
|
+
licenses:
|
68
|
+
- MIT
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
81
|
+
requirements:
|
82
|
+
- - ! '>='
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
requirements: []
|
86
|
+
rubyforge_project:
|
87
|
+
rubygems_version: 1.8.23
|
88
|
+
signing_key:
|
89
|
+
specification_version: 3
|
90
|
+
summary: Laser Rubots!
|
91
|
+
test_files: []
|