'RTanque' Part 4: Invincible

'RTanque' Part 4: Invincible

Making Any Bot a Brain Surgeon. 

How to render your opponents brainless. 

Brain surgery is not simple, at least not to me. The base brain surgery code for this bot came from a bot David Bock created called the lobotomizer. I am going to show you how you can make any bot a brain surgeon like the lobotomizer by:
  • Adding an accessor to the Bot class.
  • Adding three lines of code to your tick! method.
  • Adding two private methods at the end of your code.
 So your bot will look something like this before we make your bot a brain surgeon.  
class YourBot < RTanque::Bot::Brain
  NAME = "Your Bot's Name"
  include RTanque::Bot::BrainHelper
  TURRET_FIRE_RANGE = RTanque::Heading::ONE_DEGREE * 1.5

  def tick!
      some_function_a
      some_function_b
  end

  def some_function_a
     #logic of function a 
  end

  def some_function_b
     #logic of function b 
  end

end

First we will drill open the skull, by adding the following code.

# drill open their skulls
  class RTanque::Bot
    attr_accessor :brain
  end

This little bit of code reopens the Bot Class and allows you access to That by itself won't make you a brain surgeon but it will allow you to avoid doing brain surgery on your self. We will next edit the tick! of your bot.

  def tick!
 first_time do
      lobotomize_opponents
    end
      some_function_a
      some_function_b
  end

So now that you added this code to your bot, we also need to add the functions first_time and lobotimize_opponents that your tick! method will now call at the end of your code.

private

  def first_time
    unless @first_time_guard
      yield
      @first_time_guard = "checked"
    end
  end

  def lobotomize_opponents
    ObjectSpace.each_object(RTanque::Bot) { |x|
     x.brain.class.send(:define_method, :tick!) {
      "#This is where you write the new tick! 
      #method that you will use to replace your opponents
      #If you leave this blank or only comments"
      } unless x.brain.class == self.class
    }

  end

Let me explain tick! calls first_time every time it runs. When first_time runs for the first time will not @first_time_guard be defined so by default @first_time_guard will be nil.
The unless is basically an if not so when @first_time_guard is evaluated as nil, yield will execute which means lobotomize_opponents will be called, and @first_time_guard will be assigned "checked". When tick! runs again it will still call first_time, but @first_time_guard  is not false or nil, so yield will not execute again.
How lobotomize_opponents works is a little more complicated, and I do not fully understand it completely, but basically for every Bot it will redefine tick! to whatever string is inside the inner curly brackets.

unless x.brain.class == self.class

The above code, just makes sure you are not redefining your own bots tick! One important note if you try to make clones of this tank, they're likely to lobotomize each other. Here is what the BasicTargetingBot looks like after I made him a brain surgeon.

class BrainSurgeonBasicTargetingBot < RTanque::Bot::Brain
  NAME = "#{self}"
  include RTanque::Bot::BrainHelper

  TURRET_FIRE_RANGE = RTanque::Heading::ONE_DEGREE * 1.5

  # drill open their skulls
  class RTanque::Bot
    attr_accessor :brain
  end

  def tick!
    first_time do
      lobotomize_opponents
    end

    ## main logic goes here
    # use self.sensors to detect things
    # use self.command to control tank
    # self.arena contains the dimensions of the arena

    self.make_circles
    if we_have_target
      target = we_have_target
      track_target(target)
      aim_at_target(target)
      fire_at_target(target)
    else
      self.scan_with_radar
    end
  end

  def make_circles
    command.speed = MAX_BOT_SPEED # takes a value between -5 to 5 
    command.heading = sensors.heading + MAX_BOT_ROTATION
  end

  def we_have_target
    self.nearest_target
  end

  def nearest_target
    self.sensors.radar.min { |a,b| a.distance <=> b.distance }
  end

  def track_target(target)
    self.command.radar_heading = target.heading
  end
  
  def aim_at_target(target)
    self.command.turret_heading = target.heading
  end

  def fire_at_target(target)
      if self.pointing_at_target?(target)
        command.fire(MAX_FIRE_POWER)
      end
  end

  def pointing_at_target?(target)
    (target.heading.delta(sensors.turret_heading)).abs < TURRET_FIRE_RANGE
  end

  def scan_with_radar
    self.command.radar_heading = self.sensors.radar_heading + MAX_RADAR_ROTATION
  end

  private

  def first_time
    unless @first_time_guard
      yield
      @first_time_guard = "checked"
    end
  end

  def lobotomize_opponents
    ObjectSpace.each_object(RTanque::Bot) { |x|
     x.brain.class.send(:define_method, :tick!) {
      "#This is where you write the new tick!
      #method that you will use to replace your opponents
      #if you leave this blank you will make him brainless"
      } unless x.brain.class == self.class
    }

  end
end
This is a guide to making 'RTanque' tank bot brain surgeons.
This is Part 4 of a 4 part series on 'RTanque' tank bot making. Joshua Kemp @joshuakemp1 and myself  Cody Kemp @codesterkemp have be joint authors during this series.
We do foresee writing of future RTanque articles including "How to Avoid Brain Surgery" and "Precision Targeting" 

No comments:

Post a Comment