Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Ruby > [SOLUTION] Quiz #59 (rrobots)

Reply
Thread Tools

[SOLUTION] Quiz #59 (rrobots)

 
 
Stefan Walk
Guest
Posts: n/a
 
      12-27-2005
Hi,

here is my solution...
RubberDuckLinear is a linear shooting variant of RubberDuck and runs
significantly faster, but the extra code was neccessary to hit some of
the dodgy robots (Kite2 for example). Rubberduck-debug includes debug
code, for interested people (not a tournament entry).

Regards,
Stefan

require 'robot'
require 'ostruct'

module RubberRobot
MEMO_SIZE = 250
GUN_POWER = 0.1
ACC_WHILE_MOVING = false
class Memo # (forgetting old values) {{{
def initialize(n)
@array = []
@n = n
end
def << (arg)
@array.unshift arg
@array = @array.first(@n)
end
def [](arg)
@array[arg]
end
def to_a
@array
end
def nearest_non_nil(idx)
0.upto(51) do |offs|
next if @array[idx + ((offs % 2 == 0) ? -1 : 1)*offs].nil?
return idx + ((offs % 2 == 0) ? -1 : 1)*offs
end
nil
end
end # }}}
class Brain # {{{
include Math # This robot knows Math!
def time
@robot.time
end
def battlefield_width
@robot.battlefield_width
end
def battlefield_height
@robot.battlefield_height
end
def size
@robot.size
end
def say(string)
@robot.say(string)
end
def trim(min, val, max) # {{{
return min if val < min
return max if val > max
return val
end # }}}
def strim(val, border) # {{{
return -border if val < -border
return border if val > border
return val
end # }}}
def initialize(robot) # {{{
@memory = OpenStruct.new
@memory.rheading = Memo.new(MEMO_SIZE)
@memory.pos = Memo.new(MEMO_SIZE)
@memory.enemypos = Memo.new(MEMO_SIZE*4)
@memory.enemyrange = Memo.new(MEMO_SIZE)
@memory.enemyangle = Memo.new(MEMO_SIZE)
@memory.lastseen = nil
@robot = robot
@tlock = 0
@lasthit = 0
@looking_since = 0
@aim_mode = :linear
end # }}}
# def method_missing(name, *args) # {{{
# @robot.__send__(name, *args)
# end # }}}
def refresh # {{{
@x = @robot.x
@y = @robot.y
@turn_body = 0
@turn_turret = 0
@turn_radar = 0
@power = GUN_POWER
@accelerate = 0
@body_heading = @robot.heading
@turret_heading = @robot.gun_heading
@radar_heading = @robot.radar_heading
end # }}}
def tick(events) # {{{
refresh
save_memory
if events.has_key? 'robot_scanned'
r = events['robot_scanned'][0][0]
phi0 = @memory.rheading[0]
phi1 = @memory.rheading[1]
if (phi0 < 90 and phi1 > 270) or (phi0 > 270 and phi1 < 90)
phim = (phi0 + phi1 + 90) % 360 - 90
else
phim = (phi0 + phi1)/2
end
phi0, phi1, phim = phi0.to_rad, phi1.to_rad, phim.to_rad
dx = r*(cos(phi0) - cos(phi1)).abs/2
dy = r*(sin(phi0) - sin(phi1)).abs/2
x = @x + r * cos(phim)
y = @y - r * sin(phim)
@memory.lastseen = 0
@memory.enemypos << [x,dx,y,dy]
@memory.enemyrange << r
@memory.enemyangle << phim
@memory.enemy
else
@memory.lastseen += 1 if @memory.lastseen
@memory.enemypos << nil
@memory.enemyrange << nil
@memory.enemyangle << nil
end
if events.has_key? 'got_hit'
@lasthit = 0
else
@lasthit += 1
end
if time == 50
say "<Patrician|Away> what does your robot do, sam"
elsif time == 100
say "<bovril> it collects data about the surrounding environment, ..."
elsif time == 150
say "... then discards it and drives into walls"
end
do_radar_aiming
do_turret_aiming
do_movement
execute
end # }}}
def do_turret_aiming # {{{
if (time >= 200 and time % 100 == 0) or @schedule_calc
@schedule_calc = false
corr = check_correlation
begin
max = corr.max{|a,b| a[1] <=> b[1]}
rescue ArgumentError => e
p corr.sort
raise e
end
if max[0] > 35 and max[1] > 0.35
@aim_mode = :correlation
@corr_diff = max[0]
else
@aim_mode = :linear
end
end
case @aim_mode
when :linear
@turn_turret = 1
dist = 800
i = 0
angle = nil
x, y = nil, nil
catch :tads do
loop do
x,y = predict(dist/30.0 + 1) # TODO ???
throw :tads unless x and y
angle, distn = get_angle_and_dist(x,y)
break if (distn - dist).abs < 15 or (i += 1) > 20
dist = distn
end
@turn_turret = deg_diff(angle, @turret_heading)
end
when :correlation
=begin
``Those who cannot remember the past are condemned to repeat it.''
-- George Santayana (1863-1952)
=end
idx = @memory.enemypos.nearest_non_nil(@corr_diff - 25)
unless idx
@turn_turret = 0
@schedule_calc = true
break
end
pos = nil
angle = nil
dist = nil
i = 0
loop do
pos = @memory.enemypos[idx]
angle, dist = get_angle_and_dist(pos[0], pos[2])
idxn = @memory.enemypos.nearest_non_nil(@corr_diff - (dist/30.0).to_i)
break unless idxn
break if idxn == idx
idx = idxn
break if i >= 20
i += 1
end
x,dx,y,dy = @memory.enemypos[idx]
angle, dist = get_angle_and_dist(x, y)
@turn_turret = deg_diff(angle, @turret_heading)
else
raise 'wtf?'
end
@angle = angle
end # }}}
def do_radar_aiming # {{{
@turn_radar = 10 and return unless @memory.lastseen
@radar_turn_speed ||= 60
if @memory.lastseen == 0
@radar_turn_speed *= -0.5
else @memory.lastseen != 2
@radar_turn_speed *= -2 if @radar_turn_speed.abs < 60
end
@turn_radar = @radar_turn_speed
end # }}}
def do_movement # {{{
=begin
http://bash.org/?240849
<Patrician|Away> what does your robot do, sam
<bovril> it collects data about the surrounding environment, then discards it
and drives into walls
=end
@accelerate = 1 # Energie!
@move_dur ||= 0
@turn_dur ||= 0
@acc_dur ||= 0
if @move_dur == 0
wangle, wdist = get_revangle_and_dist_wall
if wdist < 100
@turn_dir = strim(deg_diff(wangle, @body_heading), 10)
else
@turn_dir = (rand > 0.5) ? 10 : -10
end
@turn_dur = 7 + (rand 12)
@move_dur = @turn_dur + 5 + (rand 10)
@turn_body = @turn_dir
if ACC_WHILE_MOVING
@acc_dur = rand 7
end
elsif @turn_dur == 0
@move_dur -= 1
elsif @acc_dur == 0
@move_dur -= 1
@turn_dur -= 1
@turn_body = @turn_dir
else
@acc_dur -= 1
@move_dur -= 1
@turn_dur -= 1
@accelerate = -1
@turn_body = @turn_dir
end
end # }}}
def sign(x) # {{{
return 0 if x == 0
return 1 if x > 1
return -1
end # }}}
def dist_to_center # {{{
hypot(battlefield_height/2 - @y, battlefield_width/2 - @x)
end # }}}
def angle_to_center
end
def get_revangle_and_dist_wall
return [[@x,0], [@y,270], [battlefield_width - @x, 180], [battlefield_height - @y, 90]].min.reverse
end
def approaching_center # {{{
angle = (atan2(@x, @y).to_deg + 90) % 360
diff = deg_diff(angle, @body_heading).abs
diff < 80
end # }}}
def deg_diff(ang1, ang2) # {{{
(ang1 - ang2 + 180) % 360 - 180
end # }}}
def get_angle_and_dist(x,y) # {{{
dx = x - @x
dy = y - @y
return [(atan2(dx, dy).to_deg - 90) % 360, hypot(dx, dy)]
end # }}}
def predict(ticks, maxhist = 70) # {{{
i = 0
lx = 0.0
mx = 0.0
nx = 0.0
ox = 0.0
px = 0.0
ly = 0.0
my = 0.0
ny = 0.0
oy = 0.0
py = 0.0
@memory.enemypos.to_a.each_with_index do |ev, idx|
next unless ev
break if idx > maxhist
break if i > 5
x, dx, y, dy = ev
dx *= 1 + (idx / 50.to_f) ** 2
dy *= 1 + (idx / 50.to_f) ** 2
dx = 1.0 if dx < 1.0
dy = 1.0 if dy < 1.0
t = -idx-1
i += 1
sx = dx**2
lx += t**2/sx
mx += t/sx
nx += t*x/sx
ox += x/sx
px += 1/sx
sy = dy**2
ly += t**2/sy
my += t/sy
ny += t*y/sy
oy += y/sy
py += 1/sy
end
return unless i >= 2

ax = (px*nx - mx*ox)/(px*lx - mx**2)
bx = 1/px*(mx*ax-ox)
ay = (py*ny - my*oy)/(py*ly - my**2)
by = 1/py*(my*ay-oy)

bx *= -1
by *= -1

factor = hypot(ax, ay)/8.0
if factor > 1
ax /= factor
ay /= factor
end

bx = battlefield_width - size if bx > battlefield_width - size
by = battlefield_width - size if by > battlefield_width - size
bx = size if bx < size
by = size if by < size

px = bx+ax*ticks
py = by+ay*ticks
return if px.infinite? or px.nan? or py.infinite? or py.nan?
px = [battlefield_width - size, [size, px].max].min
py = [battlefield_height - size, [size, py].max].min
[px, py]
end # }}}
def execute # {{{
@turn_turret -= @turn_body
@turn_radar -= @turn_body + @turn_turret
@turn_body = trim(-10, @turn_body, 10)
@turn_turret = trim(-30, @turn_turret, 30)
@turn_radar = trim(-60, @turn_radar, 60)
@robot.accelerate(@accelerate)
@robot.turn(@turn_body)
@robot.turn_gun(@turn_turret)
@robot.turn_radar(@turn_radar)
@robot.fire(@power)
end # }}}
def save_memory # {{{
@memory.rheading << @radar_heading
@memory.pos << [@x, @y]
end # }}}
def check_correlation # {{{
pos = @memory.enemypos.to_a[0..500]
size = @robot.size
corr_values = {}
30.upto(150) do |diff|
hits = 0
tries = 0
0.upto(pos.size - diff) do |idx|
next unless pos[idx] and pos[idx + diff]
next unless hypot(pos[idx][1],pos[idx][3]) < 100
next unless hypot(pos[idx + diff][1],pos[idx + diff][3]) < 100
dist = hypot(pos[idx][0] - pos[idx + diff][0],
pos[idx][2] - pos[idx + diff][2])
tries += 1
hits += 1 if dist < size
end
ratio = hits.to_f/tries
ratio = 0.0 unless ratio.finite?
corr_values[diff] = ratio
end
corr_values
end # }}}
end # }}}
class Robot # {{{
include ::Robot

def initialize
@my_brain = Brain.new(self)
end

def tick(events)
@my_brain.tick(events)
end
end # }}}
end

class RubberDuck < RubberRobot::Robot; end

require 'robot'
require 'ostruct'

module RubberRobot
RUBBERDEBUG = true
MEMO_SIZE = 250
GUN_POWER = 0.1
ACC_WHILE_MOVING = true
class Memo # (forgetting old values) {{{
def initialize(n)
@array = []
@n = n
end
def << (arg)
@array.unshift arg
@array = @array.first(@n)
end
def [](arg)
@array[arg]
end
def to_a
@array
end
def nearest_non_nil(idx)
0.upto(51) do |offs|
next if @array[idx + ((offs % 2 == 0) ? -1 : 1)*offs].nil?
return idx + ((offs % 2 == 0) ? -1 : 1)*offs
end
nil
end
end # }}}
class Brain # {{{
include Math # This robot knows Math!
def time
@robot.time
end
def battlefield_width
@robot.battlefield_width
end
def battlefield_height
@robot.battlefield_height
end
def size
@robot.size
end
def say(string)
@robot.say(string)
end
def trim(min, val, max) # {{{
return min if val < min
return max if val > max
return val
end # }}}
def strim(val, border) # {{{
return -border if val < -border
return border if val > border
return val
end # }}}
def initialize(robot) # {{{
@memory = OpenStruct.new
@memory.rheading = Memo.new(MEMO_SIZE)
@memory.pos = Memo.new(MEMO_SIZE)
@memory.enemypos = Memo.new(MEMO_SIZE*4)
@memory.enemyrange = Memo.new(MEMO_SIZE)
@memory.enemyangle = Memo.new(MEMO_SIZE)
@memory.lastseen = nil
@robot = robot
@tlock = 0
@lasthit = 0
@looking_since = 0
@aim_mode = :linear
end # }}}
# def method_missing(name, *args) # {{{
# @robot.__send__(name, *args)
# end # }}}
if RUBBERDEBUG # {{{ paints detection and prediction onto the canvas
def check_canvas
return false unless defined? TkCanvas
unless @canvas
ObjectSpace.each_object(TkCanvas) {|o| @canvas = o }
end
return false unless @canvas
return true
end

def detect_at(xmin, xmax, ymin, ymax)
return unless check_canvas
unless @detect_p
@detect_p = TkcOval.new(@canvas, [-5,-5], [6,6], utline => "#ff0000")
end
@detect_p.coords(xmin/2,ymin/2,xmax/2,ymax/2)
end

def predict_at(x,y)
return unless check_canvas
unless @predict_p
@predict_p = TkcOval.new(@canvas, [-5,-5], [6,6], utline => "#00ff00")
end
@predict_p.coords(x/2-3,y/2-3,x/2+3,y/2+3)
end
end # }}}
def refresh # {{{
@x = @robot.x
@y = @robot.y
@turn_body = 0
@turn_turret = 0
@turn_radar = 0
@power = GUN_POWER
@accelerate = 0
@body_heading = @robot.heading
@turret_heading = @robot.gun_heading
@radar_heading = @robot.radar_heading
end # }}}
def tick(events) # {{{
refresh
save_memory
if events.has_key? 'robot_scanned'
r = events['robot_scanned'][0][0]
phi0 = @memory.rheading[0]
phi1 = @memory.rheading[1]
if (phi0 < 90 and phi1 > 270) or (phi0 > 270 and phi1 < 90)
phim = (phi0 + phi1 + 90) % 360 - 90
else
phim = (phi0 + phi1)/2
end
phi0, phi1, phim = phi0.to_rad, phi1.to_rad, phim.to_rad
dx = r*(cos(phi0) - cos(phi1)).abs/2
dy = r*(sin(phi0) - sin(phi1)).abs/2
x = @x + r * cos(phim)
y = @y - r * sin(phim)
detect_at(x-dx,x+dx,y-dy,y+dy) if RUBBERDEBUG
@memory.lastseen = 0
@memory.enemypos << [x,dx,y,dy]
@memory.enemyrange << r
@memory.enemyangle << phim
@memory.enemy
else
@memory.lastseen += 1 if @memory.lastseen
@memory.enemypos << nil
@memory.enemyrange << nil
@memory.enemyangle << nil
end
if events.has_key? 'got_hit'
@lasthit = 0
else
@lasthit += 1
end
if time == 50
say "<Patrician|Away> what does your robot do, sam"
elsif time == 100
say "<bovril> it collects data about the surrounding environment, ..."
elsif time == 150
say "... then discards it and drives into walls"
end
do_radar_aiming
do_turret_aiming
do_movement
execute
end # }}}
def do_turret_aiming # {{{
if (time >= 200 and time % 100 == 0) or @schedule_calc
@schedule_calc = false
corr = check_correlation
begin
max = corr.max{|a,b| a[1] <=> b[1]}
rescue ArgumentError => e
p corr.sort
raise e
end
if max[0] > 35 and max[1] > 0.35
@aim_mode = :correlation
@corr_diff = max[0]
else
@aim_mode = :linear
end
end
case @aim_mode
when :linear
@turn_turret = 1
dist = 800
i = 0
angle = nil
x, y = nil, nil
catch :tads do
loop do
x,y = predict(dist/30.0 + 1) # TODO ???
throw :tads unless x and y
angle, distn = get_angle_and_dist(x,y)
break if (distn - dist).abs < 15 or (i += 1) > 20
dist = distn
end
@turn_turret = deg_diff(angle, @turret_heading)
end
if x and y
predict_at(x, y) if RUBBERDEBUG
else
predict_at(0,0) if RUBBERDEBUG
end
when :correlation
=begin
``Those who cannot remember the past are condemned to repeat it.''
-- George Santayana (1863-1952)
=end
idx = @memory.enemypos.nearest_non_nil(@corr_diff - 25)
unless idx
@turn_turret = 0
@schedule_calc = true
break
end
pos = nil
angle = nil
dist = nil
i = 0
loop do
pos = @memory.enemypos[idx]
angle, dist = get_angle_and_dist(pos[0], pos[2])
idxn = @memory.enemypos.nearest_non_nil(@corr_diff - (dist/30.0).to_i)
break unless idxn
break if idxn == idx
idx = idxn
break if i >= 20
i += 1
end
x,dx,y,dy = @memory.enemypos[idx]
angle, dist = get_angle_and_dist(x, y)
@turn_turret = deg_diff(angle, @turret_heading)
if x and y
predict_at(x, y) if RUBBERDEBUG
else
predict_at(0,0) if RUBBERDEBUG
end
else
raise 'wtf?'
end
@angle = angle
end # }}}
def do_radar_aiming # {{{
@turn_radar = 10 and return unless @memory.lastseen
@radar_turn_speed ||= 60
if @memory.lastseen == 0
@radar_turn_speed *= -0.5
else @memory.lastseen != 2
@radar_turn_speed *= -2 if @radar_turn_speed.abs < 60
end
@turn_radar = @radar_turn_speed
end # }}}
def do_movement # {{{
=begin
http://bash.org/?240849
<Patrician|Away> what does your robot do, sam
<bovril> it collects data about the surrounding environment, then discards it
and drives into walls
=end
@accelerate = 1 # Energie!
@move_dur ||= 0
@turn_dur ||= 0
@acc_dur ||= 0
if @move_dur == 0
wangle, wdist = get_revangle_and_dist_wall
if wdist < 100
@turn_dir = strim(deg_diff(wangle, @body_heading), 10)
else
@turn_dir = (rand > 0.5) ? 10 : -10
end
@turn_dur = 7 + (rand 12)
@move_dur = @turn_dur + 5 + (rand 10)
@turn_body = @turn_dir
if ACC_WHILE_MOVING
@acc_dur = rand 7
end
elsif @turn_dur == 0
@move_dur -= 1
elsif @acc_dur == 0
@move_dur -= 1
@turn_dur -= 1
@turn_body = @turn_dir
else
@acc_dur -= 1
@move_dur -= 1
@turn_dur -= 1
@accelerate = -1
@turn_body = @turn_dir
end
end # }}}
def sign(x) # {{{
return 0 if x == 0
return 1 if x > 1
return -1
end # }}}
def dist_to_center # {{{
hypot(battlefield_height/2 - @y, battlefield_width/2 - @x)
end # }}}
def angle_to_center
end
def get_revangle_and_dist_wall
return [[@x,0], [@y,270], [battlefield_width - @x, 180], [battlefield_height - @y, 90]].min.reverse
end
def approaching_center # {{{
angle = (atan2(@x, @y).to_deg + 90) % 360
diff = deg_diff(angle, @body_heading).abs
diff < 80
end # }}}
def deg_diff(ang1, ang2) # {{{
(ang1 - ang2 + 180) % 360 - 180
end # }}}
def get_angle_and_dist(x,y) # {{{
dx = x - @x
dy = y - @y
return [(atan2(dx, dy).to_deg - 90) % 360, hypot(dx, dy)]
end # }}}
def predict(ticks, maxhist = 70) # {{{
i = 0
lx = 0.0
mx = 0.0
nx = 0.0
ox = 0.0
px = 0.0
ly = 0.0
my = 0.0
ny = 0.0
oy = 0.0
py = 0.0
@memory.enemypos.to_a.each_with_index do |ev, idx|
next unless ev
break if idx > maxhist
break if i > 5
x, dx, y, dy = ev
dx *= 1 + (idx / 50.to_f) ** 2
dy *= 1 + (idx / 50.to_f) ** 2
dx = 1.0 if dx < 1.0
dy = 1.0 if dy < 1.0
t = -idx-1
i += 1
sx = dx**2
lx += t**2/sx
mx += t/sx
nx += t*x/sx
ox += x/sx
px += 1/sx
sy = dy**2
ly += t**2/sy
my += t/sy
ny += t*y/sy
oy += y/sy
py += 1/sy
end
return unless i >= 2

ax = (px*nx - mx*ox)/(px*lx - mx**2)
bx = 1/px*(mx*ax-ox)
ay = (py*ny - my*oy)/(py*ly - my**2)
by = 1/py*(my*ay-oy)

bx *= -1
by *= -1

factor = hypot(ax, ay)/8.0
if factor > 1
ax /= factor
ay /= factor
end

bx = battlefield_width - size if bx > battlefield_width - size
by = battlefield_width - size if by > battlefield_width - size
bx = size if bx < size
by = size if by < size

px = bx+ax*ticks
py = by+ay*ticks
return if px.infinite? or px.nan? or py.infinite? or py.nan?
px = [battlefield_width - size, [size, px].max].min
py = [battlefield_height - size, [size, py].max].min
[px, py]
end # }}}
def execute # {{{
@turn_turret -= @turn_body
@turn_radar -= @turn_body + @turn_turret
@turn_body = trim(-10, @turn_body, 10)
@turn_turret = trim(-30, @turn_turret, 30)
@turn_radar = trim(-60, @turn_radar, 60)
@robot.accelerate(@accelerate)
@robot.turn(@turn_body)
@robot.turn_gun(@turn_turret)
@robot.turn_radar(@turn_radar)
@robot.fire(@power)
end # }}}
def save_memory # {{{
@memory.rheading << @radar_heading
@memory.pos << [@x, @y]
end # }}}
def check_correlation # {{{
pos = @memory.enemypos.to_a[0..500]
size = @robot.size
corr_values = {}
30.upto(150) do |diff|
hits = 0
tries = 0
0.upto(pos.size - diff) do |idx|
next unless pos[idx] and pos[idx + diff]
next unless hypot(pos[idx][1],pos[idx][3]) < 100
next unless hypot(pos[idx + diff][1],pos[idx + diff][3]) < 100
dist = hypot(pos[idx][0] - pos[idx + diff][0],
pos[idx][2] - pos[idx + diff][2])
tries += 1
hits += 1 if dist < size
end
ratio = hits.to_f/tries
ratio = 0.0 unless ratio.finite?
corr_values[diff] = ratio
end
corr_values
end # }}}
end # }}}
class Robot # {{{
include ::Robot

def initialize
@my_brain = Brain.new(self)
end

def tick(events)
@my_brain.tick(events)
end
end # }}}
end

class RubberDuck < RubberRobot::Robot; end

require 'robot'
require 'ostruct'
#same as
module RubberRobotLinear
MEMO_SIZE = 250
GUN_POWER = 0.1
ACC_WHILE_MOVING = true
class Memo # (forgetting old values) {{{
def initialize(n)
@array = []
@n = n
end
def << (arg)
@array.unshift arg
@array = @array.first(@n)
end
def [](arg)
@array[arg]
end
def to_a
@array
end
def nearest_non_nil(idx)
0.upto(51) do |offs|
next if @array[idx + ((offs % 2 == 0) ? -1 : 1)*offs].nil?
return idx + ((offs % 2 == 0) ? -1 : 1)*offs
end
nil
end
end # }}}
class Brain # {{{
include Math # This robot knows Math!
def time
@robot.time
end
def battlefield_width
@robot.battlefield_width
end
def battlefield_height
@robot.battlefield_height
end
def size
@robot.size
end
def say(string)
@robot.say(string)
end
def trim(min, val, max) # {{{
return min if val < min
return max if val > max
return val
end # }}}
def strim(val, border) # {{{
return -border if val < -border
return border if val > border
return val
end # }}}
def initialize(robot) # {{{
@memory = OpenStruct.new
@memory.rheading = Memo.new(MEMO_SIZE)
@memory.pos = Memo.new(MEMO_SIZE)
@memory.enemypos = Memo.new(MEMO_SIZE*4)
@memory.enemyrange = Memo.new(MEMO_SIZE)
@memory.enemyangle = Memo.new(MEMO_SIZE)
@memory.lastseen = nil
@robot = robot
@tlock = 0
@lasthit = 0
@looking_since = 0
@aim_mode = :linear
end # }}}
# def method_missing(name, *args) # {{{
# @robot.__send__(name, *args)
# end # }}}
def refresh # {{{
@x = @robot.x
@y = @robot.y
@turn_body = 0
@turn_turret = 0
@turn_radar = 0
@power = GUN_POWER
@accelerate = 0
@body_heading = @robot.heading
@turret_heading = @robot.gun_heading
@radar_heading = @robot.radar_heading
end # }}}
def tick(events) # {{{
refresh
save_memory
if events.has_key? 'robot_scanned'
r = events['robot_scanned'][0][0]
phi0 = @memory.rheading[0]
phi1 = @memory.rheading[1]
if (phi0 < 90 and phi1 > 270) or (phi0 > 270 and phi1 < 90)
phim = (phi0 + phi1 + 90) % 360 - 90
else
phim = (phi0 + phi1)/2
end
phi0, phi1, phim = phi0.to_rad, phi1.to_rad, phim.to_rad
dx = r*(cos(phi0) - cos(phi1)).abs/2
dy = r*(sin(phi0) - sin(phi1)).abs/2
x = @x + r * cos(phim)
y = @y - r * sin(phim)
@memory.lastseen = 0
@memory.enemypos << [x,dx,y,dy]
@memory.enemyrange << r
@memory.enemyangle << phim
@memory.enemy
else
@memory.lastseen += 1 if @memory.lastseen
@memory.enemypos << nil
@memory.enemyrange << nil
@memory.enemyangle << nil
end
if events.has_key? 'got_hit'
@lasthit = 0
else
@lasthit += 1
end
if time == 50
say "<Patrician|Away> what does your robot do, sam"
elsif time == 100
say "<bovril> it collects data about the surrounding environment, ..."
elsif time == 150
say "... then discards it and drives into walls"
end
do_radar_aiming
do_turret_aiming
do_movement
execute
end # }}}
def do_turret_aiming # {{{
@turn_turret = 1
dist = 800
i = 0
angle = nil
x, y = nil, nil
catch :tads do
loop do
x,y = predict(dist/30.0 + 1) # TODO ???
throw :tads unless x and y
angle, distn = get_angle_and_dist(x,y)
break if (distn - dist).abs < 15 or (i += 1) > 20
dist = distn
end
@turn_turret = deg_diff(angle, @turret_heading)
end
@angle = angle
end # }}}
def do_radar_aiming # {{{
@turn_radar = 10 and return unless @memory.lastseen
@radar_turn_speed ||= 60
if @memory.lastseen == 0
@radar_turn_speed *= -0.5
else @memory.lastseen != 2
@radar_turn_speed *= -2 if @radar_turn_speed.abs < 60
end
@turn_radar = @radar_turn_speed
end # }}}
def do_movement # {{{
=begin
http://bash.org/?240849
<Patrician|Away> what does your robot do, sam
<bovril> it collects data about the surrounding environment, then discards it
and drives into walls
=end
@accelerate = 1 # Energie!
@move_dur ||= 0
@turn_dur ||= 0
@acc_dur ||= 0
if @move_dur == 0
wangle, wdist = get_revangle_and_dist_wall
if wdist < 100
@turn_dir = strim(deg_diff(wangle, @body_heading), 10)
else
@turn_dir = (rand > 0.5) ? 10 : -10
end
@turn_dur = 7 + (rand 12)
@move_dur = @turn_dur + 5 + (rand 10)
@turn_body = @turn_dir
if ACC_WHILE_MOVING
@acc_dur = rand 7
end
elsif @turn_dur == 0
@move_dur -= 1
elsif @acc_dur == 0
@move_dur -= 1
@turn_dur -= 1
@turn_body = @turn_dir
else
@acc_dur -= 1
@move_dur -= 1
@turn_dur -= 1
@accelerate = -1
@turn_body = @turn_dir
end
end # }}}
def sign(x) # {{{
return 0 if x == 0
return 1 if x > 1
return -1
end # }}}
def dist_to_center # {{{
hypot(battlefield_height/2 - @y, battlefield_width/2 - @x)
end # }}}
def angle_to_center
end
def get_revangle_and_dist_wall
return [[@x,0], [@y,270], [battlefield_width - @x, 180], [battlefield_height - @y, 90]].min.reverse
end
def approaching_center # {{{
angle = (atan2(@x, @y).to_deg + 90) % 360
diff = deg_diff(angle, @body_heading).abs
diff < 80
end # }}}
def deg_diff(ang1, ang2) # {{{
(ang1 - ang2 + 180) % 360 - 180
end # }}}
def get_angle_and_dist(x,y) # {{{
dx = x - @x
dy = y - @y
return [(atan2(dx, dy).to_deg - 90) % 360, hypot(dx, dy)]
end # }}}
def predict(ticks, maxhist = 70) # {{{
i = 0
lx = 0.0
mx = 0.0
nx = 0.0
ox = 0.0
px = 0.0
ly = 0.0
my = 0.0
ny = 0.0
oy = 0.0
py = 0.0
@memory.enemypos.to_a.each_with_index do |ev, idx|
next unless ev
break if idx > maxhist
break if i > 5
x, dx, y, dy = ev
dx *= 1 + (idx / 50.to_f) ** 2
dy *= 1 + (idx / 50.to_f) ** 2
dx = 1.0 if dx < 1.0
dy = 1.0 if dy < 1.0
t = -idx-1
i += 1
sx = dx**2
lx += t**2/sx
mx += t/sx
nx += t*x/sx
ox += x/sx
px += 1/sx
sy = dy**2
ly += t**2/sy
my += t/sy
ny += t*y/sy
oy += y/sy
py += 1/sy
end
return unless i >= 2

ax = (px*nx - mx*ox)/(px*lx - mx**2)
bx = 1/px*(mx*ax-ox)
ay = (py*ny - my*oy)/(py*ly - my**2)
by = 1/py*(my*ay-oy)

bx *= -1
by *= -1

factor = hypot(ax, ay)/8.0
if factor > 1
ax /= factor
ay /= factor
end

bx = battlefield_width - size if bx > battlefield_width - size
by = battlefield_width - size if by > battlefield_width - size
bx = size if bx < size
by = size if by < size

px = bx+ax*ticks
py = by+ay*ticks
return if px.infinite? or px.nan? or py.infinite? or py.nan?
px = [battlefield_width - size, [size, px].max].min
py = [battlefield_height - size, [size, py].max].min
[px, py]
end # }}}
def execute # {{{
@turn_turret -= @turn_body
@turn_radar -= @turn_body + @turn_turret
@turn_body = trim(-10, @turn_body, 10)
@turn_turret = trim(-30, @turn_turret, 30)
@turn_radar = trim(-60, @turn_radar, 60)
@robot.accelerate(@accelerate)
@robot.turn(@turn_body)
@robot.turn_gun(@turn_turret)
@robot.turn_radar(@turn_radar)
@robot.fire(@power)
end # }}}
def save_memory # {{{
@memory.rheading << @radar_heading
@memory.pos << [@x, @y]
end # }}}
end # }}}
class Robot # {{{
include ::Robot

def initialize
@my_brain = Brain.new(self)
end

def tick(events)
@my_brain.tick(events)
end
end # }}}
end

class RubberDuckLinear < RubberRobotLinear::Robot; end

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
[QUIZ] Gathering Ruby Quiz 2 Data (#189) Daniel Moore Ruby 10 01-31-2009 08:36 PM
[QUIZ] Newbie doubts about the quiz Marcelo Alvim Ruby 15 08-16-2006 02:08 PM
[QUIZ] 1-800-THE-QUIZ (#20) Ruby Quiz Ruby 15 02-24-2005 06:05 AM
[SOLUTION] Ruby Quiz #15 Animal Quiz David Tran Ruby 9 01-21-2005 02:11 AM
[QUIZ] Animal Quiz (#15) Ruby Quiz Ruby 11 01-18-2005 02:42 PM



Advertisments