views:

507

answers:

5

I had an idea of a program I want to write, but which language would be best is my problem.

If I have a car racing game and I want to allow users to submit code for new interactive 3D race tracks (think of tracks such as found in the Speed Racer movie), vehicles and for their autonomous vehicles, so, they would create the AI for their car that will enable the car to determine how to handle hazards.

So, I need a language that will run fast, and as part of a world map that the server has of all the possible races available, and their various states.

I am curious if this would be a good reason to look at creating a DSL in Scala, for example?

I don't want to have to restart an application to load new dlls or jar files so many compiled languages would be a problem.

I am open to Linux or Windows, and for the languages, most scripting languages, F#, Scala, Erlang or most OOP I can program in.

The user will be able to monitor how their vehicle is doing, and if they have more than one AI uploaded for that car, when it gets to certain obstacles they should be able to swap one AI program for another on demand.

Update: So far the solutions are javascript, using V8, and Lua.

I am curious if this may be a good use for a DSL, actually 3 separate ones. 1 for creating a racetrack, another for controlling a racecar and the third for creating new cars.

If so, would Haskell, F# or Scala be good choices for this?

Update: Would it make sense to have different parts end up in different languages? For example, if Erlang was used for the controlling of the car and Lua for the car itself, and also for the animated racetrack?

+9  A: 

Why not JavaScript or EcmaScript? Google's V8 is a really nice sandboxed way to do this. I remember it being really really easy. Of course, you will have to write some bindings for it.

Daniel A. White
Huge +1. JavaScript is absolutely the way to go. There's also the option of Rhino if you prefer a Java environment.
Bob Aman
My only problem with javascript is that it may be hard to create a language for doing graphics, such as writing the code that creates/controls the racetrack.
James Black
Javascript would be ideal though for programming the race car, for example, which is more likely to be done, so it may be that more than one language could theoretically be used.
James Black
+21  A: 

Your situation sounds like a good candidate for Lua.

  • You need sandboxing: This is easy to do in Lua. You simply initialize the users' environment by overwriting or deleting the os.execute command, for instance, and there is no way for the user to access that function anymore.
  • You want fast: Check out some of the Lua benchmarks against other languages.
  • Assumably you need to interoperate with another language. Lua is very easy (IMO) to embed in C or C++, at least. I haven't used LuaInterface, but that's the C# binding.
  • Lua has first-order functions, so it should be easy to swap functions on-the-fly.
  • Lua supports OOP to some extent with metatables.
  • Lua's primary data structure is the table (associative array) which is well-suited to sparse data structures like integrating with a world map.
  • Lua has a very regular syntax. There are no funny tricks with semicolons or indentation, so that's one less thing for your users to learn when they are picking up your language -- not to mention, using a well-documented language takes away some of the work you have to do in terms of documenting it yourself.

Also, as @elviejo points out in a comment, Lua is already used as a scripting language in many games. If nothing else, there's certainly some precedent for using Lua in the way you've described. And, as @gmonc mentions, there is a chance that your users have already used Lua in another game.


As far as how to integrate with Lua: generally, your users should simply need to upload a Lua script file. To grossly oversimplify, you might provide the users with available functions such as TurnLeft, TurnRight, Go, and Stop. Then, the users would upload a script like

Actions = {} -- empty table, but you might want to provide default functions 
function Actions.Cone()
    TurnLeft()
end

function Actions.Wall()
    Stop()
    TurnRight()
    TurnRight()
    Go()
end

Then server-side, you would might start them off with a Go(). Then, when their car reaches a cone, you call their Actions.Cone() function; a wall leads to the Actions.Wall() function, etc. At this point, you've (hopefully) already sandboxed the Lua environment, so you can simply execute their script without even much regard for error checking -- if their script results in an error, no reason you can't pass the error on directly to the user. And if there aren't any errors, the lua_State in your server's code should contain the final state of their car.


Better example

Here's a standalone C file that takes a Lua script from stdin and runs it like I explained above. The game is that you'll encounter Ground, a Fence, or a Branch, and you have to respectively Run, Jump, or Duck to pass. You input a Lua script via stdin to decide how to react. The source is a little long, but hopefully it's easy to understand (besides the Lua API which takes a while to get used to). This is my original creation over the past 30 minutes, hope it helps:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"

#define FAIL 0
#define SUCCESS 1

/* Possible states for the player */
enum STATE {
    RUNNING,
    JUMPING,
    DUCKING
};

/* Possible obstacles */
enum OBSTACLE {
    GROUND,
    FENCE,
    BRANCH
};

/* Using global vars here for brevity */
enum STATE playerstate = RUNNING;
enum OBSTACLE currentobstacle = GROUND;

/* Functions to be bound to Lua */
int Duck(lua_State *L)
{
    playerstate = DUCKING;
    return 0; /* no return values to Lua */
}

int Run(lua_State *L)
{
    playerstate = RUNNING;
    return 0;
}

int Jump(lua_State *L)
{
    playerstate = JUMPING;
    return 0;
}

/* Check if player can pass obstacle, offer feedback */
int CanPassObstacle()
{
    if ( (playerstate == RUNNING && currentobstacle == GROUND) )
    {
        printf("Successful run!\n");
        return SUCCESS;
    }
    if (playerstate == JUMPING && currentobstacle == FENCE)
    {
        printf("Successful jump!\n");
        return SUCCESS;
    }
    if (playerstate == DUCKING && currentobstacle == BRANCH)
    {
        printf("Successful duck!\n");
        return SUCCESS;
    }
    printf("Wrong move!\n");
    return FAIL;
}

/* Pick a random obstacle */
enum OBSTACLE GetNewObstacle()
{
    int i = rand() % 3;
    if (i == 0) { return GROUND; }
    if (i == 1) { return FENCE; }
    else { return BRANCH; }
}

/* Execute appropriate function defined in Lua for the next obstacle */
int HandleObstacle(lua_State *L)
{
    /* Get the table named Actions */
    lua_getglobal(L, "Actions");
    if (!lua_istable(L, -1)) {return FAIL;}
    currentobstacle = GetNewObstacle();

    /* Decide which user function to call */
    if (currentobstacle == GROUND)
    {
        lua_getfield(L, -1, "Ground");
    }
    else if (currentobstacle == FENCE)
    {
        lua_getfield(L, -1, "Fence");
    }
    else if (currentobstacle == BRANCH)
    {
        lua_getfield(L, -1, "Branch");
    }

    if (lua_isfunction(L, -1))
    {
        lua_call(L, 0, 0); /* 0 args, 0 results */
        return CanPassObstacle();
    }
    return FAIL;
}

int main()
{
    int i, res;
    srand(time(NULL));
    lua_State *L = lua_open();

    /* Bind the C functions to Lua functions */
    lua_pushcfunction(L, &Duck);
    lua_setglobal(L, "Duck");

    lua_pushcfunction(L, &Run);
    lua_setglobal(L, "Run");

    lua_pushcfunction(L, &Jump);
    lua_setglobal(L, "Jump");

    /* execute script from stdin */
    res = luaL_dofile(L, NULL); 
    if (res)
    {
        printf("Lua script error: %s\n", lua_tostring(L, -1));
        return 1;
    }

    for (i = 0 ; i < 5 ; i++)
    {
        if (HandleObstacle(L) == FAIL)
        {
            printf("You failed!\n");
            return 0;
        }
    }

    printf("You passed!\n");

    return 0;
}

Build the above on GCC with gcc runner.c -o runner -llua5.1 -I/usr/include/lua5.1.

And pretty much the only Lua script that will pass successfully every time is:

Actions = {}

function Actions.Ground() Run() end
function Actions.Fence() Jump() end
function Actions.Branch() Duck() end

which could also be written as

Actions = {}
Actions.Ground = Run
Actions.Fence = Jump
Actions.Branch = Duck

With the good script, you'll see output like:

Successful duck!
Successful run!
Successful jump!
Successful jump!
Successful duck!
You passed!

If the user tries something malicious, the program will simply provide an error:

$ echo "Actions = {} function Actions.Ground() os.execute('rm -rf /') end" | ./runner 
PANIC: unprotected error in call to Lua API (stdin:1: attempt to index global 'os' (a nil value))

With an incorrect move script, the user will see that he performed the wrong move:

$ echo "Actions = {} Actions.Ground = Jump; Actions.Fence = Duck; Actions.Branch = Run" | ./runner 
Wrong move!
You failed!
Mark Rushakoff
I thought it Lua may get a mention. I will look at it more closely and see how it works. Thank you.
James Black
Lua is already used as an embedded language in several video games.Here is a list of games using it: http://en.wikipedia.org/wiki/Category:Lua-scripted_video_gamesSo maybe LUA isn't the perfect solution. But for sure it isn't a bad one.
elviejo
Since it's really well used in other game plataforms, you may have the advantage of user's familiarity with Lua syntax, which I do believe is not the most important feature but still a good addition.
GmonC
With the voting here I will need to look more at how Lua would be used. Would I be using metalua, for example? I am actually surprised at the results so far.
James Black
+1 for Lua, my first idea too
ammoQ
+1  A: 

I had done in MMO before, you know, NPC response scripts were using python, while it is in a framework of C++, say any NPC related action will trigger the framework to run a python script (a C-python interface of course, not a shell call such as "python /script/run.py"). The scripts are replaceable runtime, though need the player or game admin to issue a command to do a refresh, but anyway the game server program is not required to restart.

Actually I forgot that whether "do a refresh by issuing a command" was required or not for a new script runtime..2 years before...but I think it suitable for you.

EffoStaff Effo
+1  A: 

Consider Erlang:

  • You need sandboxing / DSL: you can write "parser generators" to scrub access to critical/vulnerable system calls. The stock compiler can be "easily" enhanced with this functionality.

  • You need fine-grained scheduling : you have some control over this also provided you run each "user" in separate emulators. Maybe you can do better but I'd have to dig more. Remember the scheduling is O(1) :-)

  • You need resource partitioning between your "players" ( I guess if I understood correctly): Erlang has no shared-state so this helps from the on-start. You can easily craft some supervisors that watch resource consumption of the players etc. See also link on above point (lots of knobs to control the emulator).

  • You need code hot-swapping: Erlang was designed for this from the on-start

  • You need scaling: Erlang scales with SMP nicely and since it is based on message passing with seamless inter-machine communication, you can scale horizontally

  • You can optimize the critical paths using C drivers

  • Integrated "supervisor" functionality for restarting gracefully "users"

Ulf Wiger on Concurrency

jldupont
I had just recently heard of the fact that Erlang is hot swappable, which is interesting.
James Black
+2  A: 

I would recommend Dot Net for several reasons:

Players can choose which language they implement their solutions in: C#, IronPython, VB.NET, Boo, etc. but your runtime wouldn't care - it is just dynamically loading dot net assemblies into its sandbox. But this gives your players a choice of their own favorite language. This encourages players to enjoy the experience, rather than some players deciding not to participate because they simply don't like the single language that you chose. Your overall framework would probably be in C#, but players' code could be in any Dot Net language.

Sandboxing and dynamically loading are very mature in Dot Net. You could load the players' assemblies into your own sandboxed AppDomains that are running with Partial Trust. You would not have to restart the container process to load and unload these player AppDomains.

Players are encouraged to "play" this game because the language (whichever Dot Net language they choose) is not only useful for game scripting, but can lead to a real career in the industry. Doing a job search for "C#" gives a lot more hits than for "Lua" or "Haskell", for example. Therefore, the game is not only fun, but especially for younger players, is actually helping them to learn genuinely useful, marketable skills that can earn them money later. That is big encouragement to participate in this game.

Execution speed is blazing. Unlike some alternatives like Lua, this is compiled code that is well known for excellent performance, even in real-time games. (See Unity3d, for example).

Players can use MonoDevelop on Mac and Linux or they can use Visual Studio Express for free from Microsoft, or they can use good ol' notepad and the command line. The difference from the alternatives here is that mature, modern IDE's are available if players should choose to use them.

A DSL doesn't seem like a good idea for the AI parts of the problem simply because implementing AI for the vehicles is going to require a lot of creative problem solving on the part of the players. With a DSL, you are locking them into only the way that you defined the problem when you thought about it. Smart players with a complete platform like Dot Net (or some of the other choices mentioned) might have radically new and innovative solutions to some of the AI problems that you never foresaw. With the right tools, these players could implement crazy learning programs or small neural networks or who knows what in order to implement their AI. But if you lock them into a simplified DSL, there might not be much variety in different players' AI implementations (because their set of available expressions of ideas is so much smaller).

For the other parts of the problem such as defining the tracks, a DSL might be fine. Again, though, I would lean toward one of the simpler Dot Net languages like Boo simply so that you can have a unified tech stack for your entire project.

Clay Fowler
My only concern with .NET is that I would not be able to dynamically load and run, without restarting the application, and if you are just swapping one algorithm for another then I have a problem that I have to shutdown the app in order to do it. The other problem is not everyone does .NET programming, but that is a minor issue.
James Black
You could definitely dynamically load and unload without restarting the application.
Clay Fowler
And, yes, not everyone does .net (or anything else), but you could probably find more people who do one of the dot net languages than any other single, fixed language. At least players could choose one of the languages they like (or already know) this way.
Clay Fowler