Day 4: Testing and Tuning

So now that we’ve covered everything about programming a robot we want to be able to test, debug, and tune our robots so they can be all that they can be.

Testing

One of the best ways to test your robot is to make it fight every robot you have and watch its behavior. The Arena Launcher has a tool that does just this.

Launch the Arena Launcher and select the robot you want to test, in this example we will use the "Fire and Fade" (fireandfade.rs) robot that we programmed yesterday, as your Robot #1. Make sure you saved that robot in the "rscripts" folder. Then press the "Robot #1 Vs. All" button.

The "Robot #1 Vs. All" mode takes whatever robot you selected as Robot #1 and runs a match between it and all the robots in the rscripts folder. This is a good time to sit back and take notes on what your robot does (or just cringe in horror). Depending on the number of robots you have, this can take some time so you can hit escape at anytime to quit.

If you let the matches continue to the end however you will be rewarded with a list stored in the rscripts folder called: "OneVs.txt":

Games Played: 13
Filename: C:\Program Files\3DGameStudio Arena (v1)\rscripts\fireandfade.rs
Score: 9
Wins: 2
fattk3.rs(88.23) | simple mg.rs(1.10) |
Losses: 8
deadeye.rs(96.37) | simple laser.rs(71.67) | fastlaser.rs(50.42) | fback.rs(86.10) | simple cannon.rs(95.40) | simple rocket.rs(85.15) | snake rocket.rs(99.82) | sside.rs(84.91) |
Ties: 3
striker.rs(1.10) | striker2.rs(1.10) | fireandfade.rs(1.10) |
Errors!: 0
Games: 13

What this is is a list of all the games played stored under the headers of wins, losses, and ties. The numbers inside the ()’s contain the time remaining in the fight (note: due to a slight quirk with the countdown timer, games in which the time runs completely out are labeled as 1.10).

The Score is the combined score of all matches. It is a good base for comparing two robots against each other. You score the following for each match:

      Each Win: 3 pts
      Each Tie: 1 pt
      Each Loss: 0 pts
      Each Error: -6 pts

Note: If our robot produces an error during a match then it is penalized 6 points. In the actual contest it would be disqualified.

The score is also very useful for measuring performance improvements when you edit your scripts. For instance, if we made a few modifications to our robots and it scored a higher value then we probably improved the design, if the score is lower then we should try something else.

 

Writing a "Bug Log"

The action in an Arena game can be extremely fast, it’s hard to tell sometimes what functions are being called and it what order. The Game Log (gamelog.txt stored in the 3DGameStudio Arena folder) is great for reporting who damaged who and at what time:

113: Robot #2 hits the wall for 1pts of damage.
108: Robot #2 hits the wall for 1pts of damage.
101: Robot #2 hits the wall for 1pts of damage.
99: Robot #2 takes damage from Robot #1's rocket blast for 5pts of damage.
99: Robot #2 is hit with Robot #1's rocket blast for 10pts of damage.
92: Robot #1 takes damage from its own rocket blast for 10pts of damage.
90: Robot #1 gets blasted by Robot #2's laser for 15pts of damage.
90: Robot #1 gets blasted by Robot #2's laser for 15pts of damage.
89: Robot #2 takes damage from Robot #1's machine gun round for 1pts of damage.
89: Robot #2 gets blasted by Robot #1's rocket blast for 5pts of damage.
89: Robot #1 is hit with its own rocket blast for 10pts of damage.
89: Robot #2 gets blasted by Robot #1's rocket blast for 10pts of damage.
88: Robot #2 hits the wall for 1pts of damage.
78: Robot #2 hits the wall for 1pts of damage.
73: Robot #1 takes damage from Robot #2's laser for 15pts of damage.
73: Robot #2 is hit with Robot #1's machine gun round for 1pts of damage.
72: Robot #2 takes damage from Robot #1's rocket blast for 5pts of damage.
72: Robot #2 takes damage from Robot #1's rocket blast for 10pts of damage.
70: Robot #2 hits the wall for 1pts of damage.
70: Robot #2 gets blasted by Robot #1's machine gun round for 1pts of damage.
70: Robot #2 is hit with Robot #1's rocket blast for 5pts of damage.
70: Robot #1 is hit with its own rocket blast for 10pts of damage.
70: Robot #2 takes damage from Robot #1's rocket blast for 10pts of damage.
70: Robot #2 is hit with Robot #1's machine gun round for 1pts of damage.
67: Robot #2 takes damage from Robot #1's machine gun round for 1pts of damage.
66: Robot #2 is hit with Robot #1's rocket blast for 5pts of damage.
66: Robot #2 is hit with Robot #1's rocket blast for 10pts of damage.
49: Robot #1 is hit with Robot #2's laser for 15pts of damage.
49: Robot #1 gets blasted by Robot #2's laser for 15pts of damage.
Robot #1 DESTROYED!!
49: Robot #2 is victorious!!!

Total Damage Done:
      Robot#1: 110 (Self Damage: 30)
      Robot#2: 75 (Self Damage: 6)

Total Damage Taken:
      Robot#1: 105 (Collision Damage: 0)
      Robot#2: 86 (Collision Damage: 6)

But it would be nice if we could include our own debug data inside here as well.

NOTE: The following code is for debugging only!!! If you include the following commands with your submitted robot you will be disqualified!!!

The rf_game_log function can be used to write any string to the game log. It is a simple instruction that writes a string to the current game log. For instance, we can use it in our Fire and Fade robot as so:

// aim at target
function FUNC_TWO()
{
   apply_brake = OFF;

         if((los_to_target == 1) && (gun1_status >= 1))
         {
            // ready to shoot
            value6 = 1;
//********
rf_game_log("Ready to fire!\n"); // REMOVE BEFORE RELEASE
//********
            return;
         }
      }
   }
   value6 = 0; // do not have a shot to fire
   return;

}

NOTE: The left justification of the rf_game_log line is not a typo. I made this line stand out because I want to remember to remove it before submitting it to the contest.

Now the Game Log reads like this:

113: Robot #2 hits the wall for 1pts of damage.
108: Robot #2 hits the wall for 1pts of damage.
101: Robot #2 hits the wall for 1pts of damage.
Ready to fire!
99: Robot #2 takes damage from Robot #1's rocket blast for 5pts of damage.
99: Robot #2 is hit with Robot #1's rocket blast for 10pts of damage.
Ready to fire!
92: Robot #1 is hit with its own rocket blast for 10pts of damage.
90: Robot #1 takes damage from Robot #2's laser for 15pts of damage.
90: Robot #1 gets blasted by Robot #2's laser for 15pts of damage.
Ready to fire!
89: Robot #2 gets blasted by Robot #1's machine gun round for 1pts of damage.
89: Robot #2 is hit with Robot #1's rocket blast for 5pts of damage.
89: Robot #1 gets blasted by its own rocket blast for 10pts of damage.
89: Robot #2 is hit with Robot #1's rocket blast for 10pts of damage.
88: Robot #2 hits the wall for 1pts of damage.
78: Robot #2 hits the wall for 1pts of damage.
Ready to fire!
73: Robot #1 is hit with Robot #2's laser for 15pts of damage.
73: Robot #2 is hit with Robot #1's machine gun round for 1pts of damage.
72: Robot #2 gets blasted by Robot #1's rocket blast for 5pts of damage.
72: Robot #2 takes damage from Robot #1's rocket blast for 10pts of damage.
70: Robot #2 hits the wall for 1pts of damage.
Ready to fire!
70: Robot #2 is hit with Robot #1's machine gun round for 1pts of damage.
70: Robot #2 gets blasted by Robot #1's rocket blast for 5pts of damage.
70: Robot #1 takes damage from its own rocket blast for 10pts of damage.
70: Robot #2 is hit with Robot #1's rocket blast for 10pts of damage.
70: Robot #2 takes damage from Robot #1's machine gun round for 1pts of damage.
Ready to fire!
67: Robot #2 is hit with Robot #1's machine gun round for 1pts of damage.
66: Robot #2 is hit with Robot #1's rocket blast for 5pts of damage.
66: Robot #2 is hit with Robot #1's rocket blast for 10pts of damage.
49: Robot #1 gets blasted by Robot #2's laser for 15pts of damage.
49: Robot #1 gets blasted by Robot #2's laser for 15pts of damage.
Robot #1 DESTROYED!!
49: Robot #2 is victorious!!!

 
Total Damage Done:
      Robot#1: 110    (Self Damage: 30)
      Robot#2: 75    (Self Damage: 6)

Total Damage Taken:
      Robot#1: 105    (Collision Damage: 0)
      Robot#2: 86    (Collision Damage: 6)

So what does this tell us? Well we only had six shots in 70 seconds. Maybe we need to fire more and run away less?

 

Real Time Debugging

Okay now it’s time to get really tricky. The following trick will require you to make two small changes to the arena.wdl file in the Arena folder.

NOTE: It goes without saying that the following tricks are not legal in a contest robot. Like the code above, make sure to remove all debugging lines and restore your arena.wdl file back to its original form before submitting your robots.

Open your Arena folder and make a backup copy of arena.wdl. Then open it in your favorite plain text editor (i.e. Notepad) and remove the comments before the following two lines (in blue):

path ".\\code"; // path to WDL code base
path ".\\images"; // path to WDL images
path ".\\rmodels";// path to robot models

INCLUDE <debug.wdl>; // --- comment out before release

and

/////////////////////////////////////////////////////////////////
// The main() function is started at game start
function main()
{
set_debug(); //--- comment out before release

Save the changes and run a level. If you did everything right you should see a line of numbers at the bottom of the screen:

fps    #1      #2      #3       #4      #5      #6       #7      #8      #9
30      0       0       0        0       0       0        0       0       0

The first number is the frames per second. But the other nine are test values which you can set in your own code to monitor values inside your robot.

For example, maybe we want to know what ‘mode’ our robot is in and what value is stored in its counter. We can do this by adding the following lines:

function AI_ROBOT()
{
   // value1 = state
   // value2 = counter
tst_value_1 = value1;
tst_value_2 = value2;

Now we can watch these two values change as our robot plays the game. This is a handy way to look inside our robots brain while it is running and see what state it was in when it decided to do something stupid.

You can use all nine values (tst_value_1 to tst_value_9) but make sure to remove all of these and test your robot again with the original arena.wdl script before submitting it to the contest.

 

Tuning

Tuning a robot is probably 90% of the challenge. Sometimes the difference between good AI and bad AI is just a little "value tweaking". Once you have a basic strategy that you think should work you will probably spend a lot of time adjusting values to make it perform correctly. Here are three hints that should make this experience as quick and productive as possible:

Test Early, Test Often:
Once your basic robot has been designed and is running, use the tools above to test your robot whenever you make a change. Too often I find myself saying "I need to tweak these three values" and then I test the robot and no matter the result (better, worse, or the same) I end up wondering "Which value caused that change?"

For example, in our Fire and Fade robot maybe we want to increase the number of shots it fires before running, reduce its running time to 8 seconds, and increase the range at which it stops before aiming. If our robot performs a little better after doing this which one of these values helped? Did one value help while the other two actually hurt?

If you make a single change and test it then you will know exactly what caused the result. This makes it much easier to tune a robot.

Use the "Double or Half" Method:
Often times we spend a great deal of time adjusting values by some random amount, testing our changes, and then adjusting everything again by another random amount. This can be very time consuming especially if the testing process is very long.

For example, we want to optimize the number of ticks our robot will run for an opponent. Right now it is 160 ticks so we change it to 159 and test. That seams to work okay so we change it to 158 to see if we get a bigger improvement. Lets say we continue this until we find a optimal solution (or a "local maximum" if you want to be technical) at 100, because at 99 the robot started to behave worse.

The above example is fine, but it took us 61 test cycles to get down to the number 100. But if each one of those tests took 30 minutes we would spend over 30 hours to find that one value. Obviously we wouldn’t test things this way; we would take larger time steps until we narrowed down the solution. The question is what size of step should we take?

This is where the double/half method becomes useful. Take your current value, if you think it is too high, halve it. If the value is too low, double it. Continue this over the resulting range until you get a solution that works.

Using this rule in our example test case we first take half of 160 which gives us 80. We test it and it is better so we take half of 80 (40). This result is worse then 80 so we take half of the upper range, 80 – 160 (120) and test it. The result it better than 80 so we check the upper part of the range 120 – 160. 140 is worse then 120 so we check the lower half (80-120). 100 is better then 120, in fact it’s the best solution but just to make sure we do a couple of checks on the lower and upper bounds, taking half the range each time.

By using the double half rule we can find a solution in 7-10 tries rather than 61! A huge savings in time.

Tuning is Not an Exact Science
Tuning is sometimes also known as ‘tweaking’ and is, by its very nature, not an exact science. Everything in the game is carefully set to product the exact result given the same input (this isn’t always true, but it normally behaves this way) so, in theory, you could ‘solve’ the robot AI problem producing a robot with optimal winning patterns. In practice there are just too many variables for such a solution to be found in anything close to a reasonable amount of time (say, a million years).

So it is up to you to use your best guesses, backed up by plenty of testing, to get the results that put you in the winner circle. Don’t get frustrated when a simple change causes your robot to perform a hundred times worse then it did before. Make backups of your robots, and enjoy the challenge.



Conclusion:

That’s it! We covered everything you need to know to design, code, test, and tune your robots. Now it is up to you to use this knowledge to produce a winning robot. Don’t be afraid to make mistakes. Keep backups of all your robot designs (they can be used as test cases). And check the User Forum for more hints and answers.

Good luck.