Skip to content

Instantly share code, notes, and snippets.

@fimmtiu
Last active August 29, 2015 14:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fimmtiu/6556c5518e5af1837079 to your computer and use it in GitHub Desktop.
Save fimmtiu/6556c5518e5af1837079 to your computer and use it in GitHub Desktop.
shazbot
TICKS_PER_SECOND = 60
# A heading modified with a certain amount of randomness.
def heading_with_slop(heading, sloppiness_radians)
heading = heading.radians if heading.respond_to?(:radians)
left_side = heading - sloppiness_radians / 2
RTanque::Heading.new(left_side + rand * sloppiness_radians)
end
# A data structore for keeping track of radar contacts.
class Contact
CONTACT_EXPIRY = 3.0
DISTANCE_FACTOR = 100
extend Forwardable
attr_accessor :reflection, :time
def_delegators :@reflection, :heading, :distance, :name
def initialize(reflection, time)
@reflection = reflection
@time = time
end
def radar_heading(current_time)
heading_with_slop(heading, radar_slop(current_time))
end
# Increase the cone based on the distance to the target and how
# long it's been since we spotted it.
def radar_slop(current_time)
distance_adjustment = distance / DISTANCE_FACTOR
Math::PI / (15 - seconds_since_contact(current_time) + distance_adjustment)
end
def seconds_since_contact(current_time)
(current_time - time) / TICKS_PER_SECOND
end
def expired?(current_time)
seconds_since_contact(current_time) > CONTACT_EXPIRY
end
end
class Shazbot < RTanque::Bot::Brain
SENSOR_REFRESH_INTERVAL_SECONDS = 0.5
SENSOR_REFRESH_INTERVAL = (SENSOR_REFRESH_INTERVAL_SECONDS * TICKS_PER_SECOND).to_i
HEADING_REFRESH_INTERVAL_SECONDS = 1
HEADING_REFRESH_INTERVAL = (HEADING_REFRESH_INTERVAL_SECONDS * TICKS_PER_SECOND).to_i
PRECISION_DISTANCE = 450
NAME = 'shazbot'
include RTanque::Bot::BrainHelper
attr_accessor :contacts, :radar_heading_target, :direction
def tick!
update_contacts
sense_stuff
shoot_stuff
run_around
end
def random_heading
RTanque::Heading.rand
end
# If we pick a new heading for the radar too often, it just twitches madly in place. We only want
# to do so every half-second or so.
def move_radar?
radar_heading_target.nil? || closest_contact || sensors.ticks % SENSOR_REFRESH_INTERVAL == 0
end
# Every second, there's a 50% change that we'll change the direction of our forward curve.
def change_heading?
sensors.ticks % HEADING_REFRESH_INTERVAL == 0 && rand(2) == 0
end
# Update our internal list of radar contacts.
def update_contacts
@contacts ||= []
contacts.reject! {|c| c.expired?(sensors.ticks) }
sensors.radar.each do |scanned_bot|
next if scanned_bot.name == NAME # Ignore friendly tanks
contacts.reject! {|c| c.name == scanned_bot.name }
contacts << Contact.new(scanned_bot, sensors.ticks)
end
end
def closest_contact
contacts.sort_by(&:distance).first
end
# Returns which edge of the map we're currently touching.
def touching_edge
return RTanque::Heading::NORTH if sensors.position.y == arena.height
return RTanque::Heading::SOUTH if sensors.position.y == 0
return RTanque::Heading::EAST if sensors.position.x == arena.width
return RTanque::Heading::WEST if sensors.position.x == 0
nil
end
def within_90_degrees?(a, b)
a.delta(b) < Math::PI / 2
end
# We try not to drive or point the radar off the edge of the screen, because that's useless.
def crappy_heading?(heading)
edge = touching_edge or return false
within_90_degrees?(heading, edge)
end
# We start by sensing in random directions. If we have a contact, we scan in a
# PI/(10 - seconds since last seen) cone around them.
def sense_stuff
if move_radar?
if closest_contact
self.radar_heading_target = closest_contact.radar_heading(sensors.ticks)
else
begin
self.radar_heading_target = random_heading
end while crappy_heading?(radar_heading_target)
end
end
command.radar_heading = radar_heading_target
end
# Shoot directly at the target, at maximum power.
def shoot_precisely
command.turret_heading = closest_contact.heading
command.fire(31337)
end
# Spray rapidly in a cone around the target, at minimum power.
def shoot_randomly
command.turret_heading = radar_heading_target
command.fire(1)
end
def shoot_stuff
if closest_contact && closest_contact.distance < PRECISION_DISTANCE
shoot_precisely
else
shoot_randomly
end
end
# Go in a curve, randomly changing directions once in a while.
# Immediately come about if you run into the edge of the map.
def run_around
@direction ||= 1
if change_heading?
if crappy_heading?(sensors.heading)
command.heading = -sensors.heading
else
self.direction = -direction
end
end
command.heading = sensors.heading + (Math::PI / 25) * direction
command.speed = 11 # This one goes up to 11.
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment