C Robots for the web
Background
I have always been fascinated by the idea behind crobots also inspired by RobotWar on the Apple II. A simple robot simulation environment where you can program your own autonomous robot in a simple language and then battle against other AI robots.
I started a project in Java (jrobot
) where the idea was that each robot would be a JAR file which which could be loaded by the simulator (on any platform) and keep its own updated state after competitions.
Another improvement would be to support many programming languages. One common programming API nowadays are the HTTP. So the next idea is to build a simulation environment over a HTTP api. As any programming language can support the web this would make it very easy to build robots in different languages.
Web Robots
So the simulation environment is accessed over an HTTP api.
Simulation is divided in three phases.
- Simulation setup - create simulation environment, add robots and start simulation
- Battle - each robot move, scan, fire and robot state can be accessed
- Finalize - winner is determined and detailed simulation data can be retrieved
Simulation is time based where missed deadlines will result in missed opportunity to control the robot.
A separate program can be used to visualize the battle from the detailed simulation data.
HTTP API
Setup
- Create battleground - return name and time limit
- Enter (robot) battleground - return session id
Battle
- Walk - walk forward +/- x steps
- Turn robot - turn +/- x degrees
- Turn cannon - turn +/- x degrees (relative robot)
- Scan - scan for other robots in a 30 degree angle (relative cannon)
- Fire - fire cannon
- Locate - return current position, angle of robot and cannon
Finalize
- Download simulation data
How to synchronize clients over HTTP?
Each client must be allowed to send its command and get a result in each time synk.
Because the result of each simulation step is based on the actions of the other opponents we need to wait until we get all commands before we can return a result. If one client doesn't send a command within a timeout period that client will miss the opportunity to command the robot during that time slot.
This requirement doesn't work well with the typical HTTP request/response scheme where a request is assumed to get a response directly.
One solution would be to poll for the result after a command was sent.
send command
poll for result -> no result (all clients has not sent a command)
poll for result -> no result
poll for result -> return result
This would work in most web servers even non-concurrent ones like sinatra.
Another strategy would be to block each request until the last client sent its requests and then return the result from the simulation step. This would require a concurrent web server and some synchronization between request handlers on the server side.
send command -> return result
The advantage with this solution is that polling is avoided and the result can be returned directly in the command request. This also makes it possible to run the simulation faster. A new command can be sent directly after the first request returned. Polling also means wasted resources on both the client and server which can be avoided.
But on the other hand this puts additional requirements on the web server capabilities.
Yaws is a Erlang web server which seems to be capable enough.
"Yaws is entirely written in Erlang, and furthermore it is a multithreaded webserver where one Erlang lightweight process is used to handle each client."
Process commands
We assume a persistent database accessible for all request handlers.
The following tables would be needed:
- Robot
- Battleground (robots, settings)
- Simulation Step (state, result)
- Command (step, type, robot, pid of handler)
A command request could be handled the following way:
- Store command including pid of receiving http handler process and association with current simulation step
- Try a simulation step (thread). IF simulation step can be calculated (all clients has sent a command), notify all handlers on the result.
- Wait for (receive) for simulation result (from simulation step handler)
- Return simulation result.
Alternatively the responses could be delegated to a simulation handler. Then http request handlers may exit and the simulation handler take over to send all responses. But I don't know if responses can be delegated to another process.
The http request handler
- Get which
Battleground
the received command is associated with - Get current
SimulationHandler
(if none create a new one) - Send response handler and command to simulation handler
And in the SimulationHandler
...
- Wait for (receive) simulation commands with a timeout
- Update simulation state in mnesia
- Return simulation results for all commands
Code sketch
Sketch of code. Does it work now?
Battle
id = 123
size = (100, 100)
commandTimeout = 1000 [ms]
Robot
id = 456
position = (x, y) -- position on battleground
angle = Angle -- angle of tank
aim = Angle -- angle of tower relative tank
Error
code = 234
message = "battle has not started"
Battle
create :: Battle
enter :: BattleId -> Robot -- get initial robot
start :: BattleId -> [Robot]
Wait
ping :: RobotId -> Timeout -- ping to start
Play
walk :: RobotId -> Steps -> Robot -- available commands
turn :: RobotId -> Angle -> Robot
aim :: RobotId -> Angle -> Robot
scan :: RobotId -> Robot -> Int
fire :: RobotId -> Robot
Simulation Tracer
Visualize a battle from simulation data.
This will probably be implemented in Elm language as fat web client.
Current implementation
This is under active developement..
See latest implementation here: RobotWars
References
- crobots
- RobotWar
- C-Robots
- Erlang might be very suitable for the simulation server
- Multiplayer Game Server for Turn-Based Mobile Games in Erlang
- jiffy json parser
- jsx json parser
- erlang stdlib
- Haskell Warp - seems to handle requests in separate threads
- Elm changed my mind about unpopular languages
- Scaling Elixir
- Erlang in Anger