snake oil

SnakeOil Virtual Motor Sports Lubricants.™

2015 Competition Results

After sending in my submission for 2013 I continued to work on my bot. I had started planning how I could use my cluster to optimize the parameters. I made some decent progress and was ready for 2014. I awaited the announcement of the competition and it never came. There was no event in 2014. This was very frustrating and I pretty much closed the project. But then in 2015 only months before the deadline, I learned that there was to be an event for 2015. I scrambled to disinter my work.

There were some problems however. I could not get a definitive description of the kinds of tracks I could expect. There are some pretty wild dirt tracks that come with TORCS that had never been used which I did not train for. Also the track server that had been a repository of the computer generated tracks like the ones the competition usually featured was not quite working. Fortunately, I was able to pick through the broken web code and download all of the tracks. With thousands of tracks to train from I felt pretty good. Unfortunately the competition used tracks that I’ve never heard of and couldn’t obtain so it’s difficult for me to really get a good impression of exactly what went wrong. And what went right.

I did win the "2nd runner up" prize (i.e. 3rd place) of 100 Australian dollars which was duly delivered. Thanks to the organizers for that.

Here is the official results page but it’s not much to look at. I don’t think there was a nice summary video made as there was for previous competitions.

After this competition I was pretty tired from the effort. It’s likely they won’t have another one of these SCR competitions and I’m probably too burned out on it anyway. But it was definitely a very valuable learning experience.

2013 Competition Results

The SnakeOil interface was created for my entry in the 2013 Simulated Car Racing Championships. It performed fine and my driver did ok coming in 4th, the middle. Here are some highlights from the organizers showing the finals racing. I have added my own annotations.

The AI I entered, i.e. shown in the video, was not the best car that I developed in 2013, but I was behind schedule at the deadline and was going to save my substantial improvements for 2014. Unfortunately, the organizers decided not to hold the event in 2014. :-/ I may try to get the final bot running again and give it a proper demonstration. The AI in this video is pretty much all hand tuned while subsequent work went into extensive genetic optimization. That made a huge difference and I was planning on using this final car optimized on the Linux cluster I manage for 2014. I am convinced that optimization is critical to success and I believe that Autopia, the undefeated champion for many years, derives its success from this approach.

For 2014, I’m working on my C.A.R. Project.

Introduction

SnakeOil is a Python library for interfacing with a TORCS race car simulator which has been patched with the server extensions used in the Simulated Car Racing competitions. To get a very nice introduction to the Simulated Car Racing scene you can watch the talk by Python SCR enthusiast Tennesse that inspired me to participate.

SnakeOil Car

With SnakeOil you can very easily write an entry for an autonomous race car without dealing with any messy administrative details or regrettable programming languages. It allows you to focus exclusively on the mechanics of driving the car and not of messing around with implementation details.

Download

snakeoil.py Size: 12.5kB Version: 20130506

Usage

To use it, you must import it and create a "drive()" function. This will take care of option handling and server connecting, etc. To see how to write your own client do something like this which is a complete working client:

A Test Of The SnakeOil Library
#!/usr/bin/python
import snakeoil
if __name__ == "__main__":
    C= snakeoil.Client()
    for step in xrange(C.maxSteps,0,-1):
        C.get_servers_input()
        snakeoil.drive_example(C)
        C.respond_to_server()
    C.shutdown()

This should then be a full featured client. The next step is to replace snakeoil.drive_example() with your own.

But Wait, There’s More

The Client object, C in the above example, holds various values and functionality needed to establish a complete client. There are many useful things you could do with this. This class can be used to make several clients that could all race at the same time from one program.

Here’s a complete example of a program that will spawn 4 clients to control 4 different cars.

teamrace.py
#!/usr/bin/python
import snakeoil
if __name__ == "__main__":
    Cs= [ snakeoil.Client(p=P) for P in [3001,3002,3003,3004] ]
    for step in xrange(Cs[0].maxSteps,0,-1):
        for C in Cs:
            C.get_servers_input()
            snakeoil.drive_example(C)
            C.respond_to_server()
    else:
        for C in Cs: C.shutdown()

This program will make 4 identical cars on ports 3001-3004. This makes 4 cars with identical behavior.

Synchronized Driving

teamrace.py

Of course, you don’t have to structure all 4 to use the same drive() function. You can easily have competitions between different drive functions available to the program.

Another thing to note about multiple Client objects is that the settings for each client are controllable. There are default settings if you do not do anything. Then there are global overrides that can be specified as command line options. Finally each client object can have overrides at object creation. This is how the teamrace.py program sets a different port for each client. Note that for things like host and port that need to be known at connection time, these need to be established early on. When instantiating a class, you can used named parameters to specify settings specific to that client. The parameters are named the same way as the short options (see --help). For properties that could change mid-race, you can simply set them any time through the object members. For example:

Cs[3].debug= True

Would turn on debugging for the client on 3004. It might be handy to turn on debugging only for a client that is stuck or passing, etc.

Available Data

There are many member variables that contain important things to the client. Many of these can be specified as options (try the --help). Many of these things won’t be of any concern to a bot creator. Mainly the trackname and stage are important when developing a strategic bot though you can safely ignore those too at first.

This object also contains a ServerState object (S) and a DriverAction object (R for response). This allows you to get at all the information sent by the server and to easily formulate your reply. These objects contain a member dictionary d (for data dictionary) which contain key value pairs based on the server’s syntax. These aren’t even hard coded but are formed from the server’s strings and can therefore, in theory, accommodate future changes. With the current (2013) SCR server, you can read the following:

angle, curLapTime, damage, distFromStart, distRaced, focus,
fuel, gear, lastLapTime, opponents, racePos, rpm,
speedX, speedY, speedZ, track, trackPos, wheelSpinVel, z

The syntax specifically would be something like:

X= C.S.d['tracPos']

And you can set the following:

accel, brake, clutch, gear, steer, focus, meta

The syntax is:

C.R.d['steer']= X
Note
It is steer and not steering as described in the SCR manual!

All values should be sensible for their type, including lists being lists. See the SCR manual for details or my TORCS/SCR notes.

Writing A drive() Function

The main work to be done when using SnakeOil is to write a drive function. This is pretty much exclusively focused on the business of controlling the car. All of the car’s inputs are available and all of the driver’s outputs can be specified. Basically for the conditions that present themselves to the function, you need to formulate a set of outputs that will result in the car doing The Right Thing.

The nice thing about this way of developing is that you can try many different drive() functions easily while keeping the base the same (i.e. snakeoil.py).

Here is the sample drive function from the library. This has many bugs and is quite simplistic, but it will give you some idea of how to get going.

A Sample drive() Function
def drive_example(c):
    '''This is only an example. It will get around the track but the
    correct thing to do is write your own `drive()` function.'''
    S= c.S.d
    R= c.R.d
    target_speed=100

    # Damage Control
    target_speed-= S['damage'] * .05
    if target_speed < 25: target_speed= 25

    # Steer To Corner
    R['steer']= S['angle'] * 10 / snakeoil.PI
    # Steer To Center
    R['steer']-= S['trackPos'] * .10
    if R['steer'] < -1: R['steer']= -1
    if R['steer'] >  1: R['steer']=  1

    # Throttle Control
    if S['speedX'] < target_speed - (R['steer']*50):
        R['accel']+= .01
    else:
        R['accel']-= .01
    if S['speedX']<10:
       R['accel']+= 1/(S['speedX']+.1)

    # Traction Control System
    if ((S['wheelSpinVel'][2]+S['wheelSpinVel'][3]) -
       (S['wheelSpinVel'][0]+S['wheelSpinVel'][1]) > 5):
       R['accel']-= .2
    if R['accel'] < 0: R['accel']= 0
    if R['accel'] > 1: R['accel']= 1

    # Automatic Transmission
    R['gear']=1
    if S['speedX']>50:
        R['gear']=2
    if S['speedX']>80:
        R['gear']=3
    return

Notice how the only things you need to provide code for are related directly to your car’s performance.

Here is the driving behavior of this simple routine. Definitely a good starting point.

Stand Alone

If you just run the snakeoil.py base library itself it will implement a serviceable client with a demonstration drive function that is sufficient for getting around most tracks. Try snakeoil.py --help to get started.

Tragically Duplicated Effort

When I became interested in the SCR, I assumed I’d write a C client but when I saw how much text parsing there was I thought it would just be easier to write it from scratch in Python. I naturally assumed that there was no Python client (no one had submitted one from the previous years' competitions that I was made aware of). I was wrong.

SimplePythonClient

The first Python client that the SCR organizers seem to have been aware of is Mr.Fish’s which seems to have been developed at exactly the same time I developed SnakeOil. I believe this client is a more faithful port of the C++ version written by the organizers of the SCR competitions. Unfortunately that’s not necessarily ideal for someone just starting out.

There is no option parsing and I found it to be quite a challenge to get running. The test bot will comfortably make it around the track.

pyScrcClient

Tennessee tells us about yet another Python client called pyScrcClient. I was not able to download an archive of this client but there are only 5 files and there were direct links on github so it wasn’t so bad.

I was pleased that this client had option parsing (using argparse, make sure you don’t have an ancient Python version). Like SnakeOil it also only imports sys, socket, and the parsing module keeping it lean.

The default options worked right away and I was able to immediately race with this client. It’s more aggressive than SimplePythonClient. It is definitely a great example of using Python for SCR. I wish I had known about it before I started SnakeOil.

Any More?

By the way if you have a Python client that you think would be helpful to share, I am interested in hosting it for you so that it can be downloaded easily from here.

And if you’re the author of SimplePythonClient or pyScrcClient I’ll host those too if you like. Let me know.