-
Notifications
You must be signed in to change notification settings - Fork 6
SimpleBots
Now that you have mastered your first steps, its time for more programming examples. Since you'll do your programming in Lua, be sure to open the Programming in Lua online book and the Lua 5.1 Reference Manual, since the following introduction will not explain the Lua language, only its use within the game.
Upload the following code:
function Creature:main()
self:screen_message("Bot " .. self.id)
end
It will display different messages for each Bot. self.id
refers to the internal Creature id set by the server. During its live time, the id of a creature will not change. This id can be used in many of of functions the game provides you.
Lets have a look at the following code:
function Creature:main()
for i = 1, 10 do
self:wait_for_next_round()
end
self:suicide()
end
This code loops ten times and executes the self:wait_for_next_round
function. This function needs explanation: Although the game feels like a realtime game, it is divided into small rounds of 100ms each. Your code must allow the game to advance, once you executed all commands needed for the current round, by executing the self:wait_for_next_round
function. It returns control to the game. The game then advances and, for example, moves your creatures. In the next round the call to self:wait_for_next_round
returns and you can execute code for the next round.
Each round lasts 100ms. The above loop therefore waits 1 second (10 *
100ms) and finally executes self:suicide
.
Both self:wait_for_next_round
and self:suicide
are defined in oo.lua (the Highlevel API). There you can find the following definition of the suicide
function which gets executed by calling self:suicide
:
function Creature:suicide()
suicide(self.id)
end
As you can see, the Highlevel function suicide
is defined within the class Creature. Once you call it, using self:suicide
it calls the Lowlevel function suicide
provided by the game. suicide
expects the id of the creature you want to suicide. Of course the game only allows you to suicide your own creatures :-)
You lose 40 points each time one of your creatures suicides. If your score gets below 500 points, the game will kick you. Rejoin the game and create a new player (by typing j) once this happened.
Lets upload a code that lets your creatures walk to random locations. Upload the following code:
function Creature:main()
local x1, y1, x2, y2 = world_size()
while not self:set_path(math.random(x1, x2), math.random(y1, y2)) do
end
self:begin_walk_path()
while self:is_walking() do
self:wait_for_next_round()
end
end
The function world_size
returns 4 values: The coordinates limiting the world. Lets have a look at the following image:
In the upper left corner are four coordinates (0,0 255,0 255,0 and 255,255). These are the minimum and maximum coordinates of the most upper left tile. One tile is always 256x256 in size. x and y increase right and downwards. Lets see what world_size
returns by using the lua shell within the game. Type l (that's a lowercase L) to enter it.
Type print(world_size())
to execute the world_size
function the game provides you. Finally hit to leave the lua shell.
> l
lua(250)> print(world_size())
256 256 16383 11775
lua(251)>
>
As you can see the top left coordinate is 256,256, since the border tiles are never walkable. We then use math.random
, which is a function Lua provides to get a random value for both x and y. Then we pass the se values to self:set_path
. This function sets the target for all movements. It returns a boolean indicating whether the coordinate passed is reachable. We therefore repeat setting random targets until one of them is reachable.
Having set a target, we can now tell our creature to actually move to that target. We use the begin_walk_path
function for that. begin_walk_path
is a Highlevel function defined in oo.lua. Lets see what is does:
function Creature:begin_walk_path ()
return set_state(self.id, CREATURE_WALK)
end
As you can see, it calls the game function set_state
, passing the id
of the creature and a numeric value CREATURE_WALK. This changes the state of the creature to walking. The state of a creature determines its action for the current round. By setting the state to CREATURE_WALK
the creature will walk to the target previously set by set_path
once you call wait_for_next_round
. If the creature reaches its destinations, it changes its state back to CREATURE_IDLE
automatically. We can use the Highlevel function is_walking
to test if the creature is walking (by testing if its state is CREATURE_WALK
):
function Creature:is_walking ()
return get_state(self.id) == CREATURE_WALK
end
Until the creature hasn't reached its target, is_walking
will return true
and we wait_for_next_round
. This will allow the creature to move.
Once is_walking
returns false
, the creature has reached its target and has therefore changed its state back to CREATURE_IDLE
. The while loop terminates and the main
function returns. It is automatically restarted in the next round, so we have creatures walking randomly on the map.
Lets get some information on both of them. Type i to get information on your creatures.
> i
<creature 20 [12825,4798] type 0, health 77, food 0, state walk>:
------------------------------
thread status : alive
stack traceback:
./api/oo.lua:293: in function 'wait_for_next_round'
[string "paste 252 from client 1"]:7: in function 'main'
./api/oo.lua:274: in function <./api/oo.lua:271>
(tail call): ?
./api/oo.lua:271: in function <./api/oo.lua:254>
<creature 21 [3124,2254] type 0, health 77, food 0, state walk>:
------------------------------
thread status : alive
stack traceback:
./api/oo.lua:293: in function 'wait_for_next_round'
[string "paste 252 from client 1"]:7: in function 'main'
./api/oo.lua:274: in function <./api/oo.lua:271>
(tail call): ?
./api/oo.lua:271: in function <./api/oo.lua:254>
> _
Some status information is displayed for each creature. You see, that both of your creatures are type 0 creatures (See CreatureTypes for more information), both of them have 77% health and are in state walk
. You see a stack traceback for both of your creatures. The code for each of your creature is executing fine, as can be seen be the status alive
.
Lets upload code that raises an error:
function Creature:foobar()
error("argh")
end
function Creature:main()
self:foobar()
end
Type r (for restart) in the prompt to force a restart of the main
function. Lets check the creature information again. Type i:
> i
<creature 4 [8391,4736] type 0, health 53, food 0, state idle>:
------------------------------
current message: [string "paste 253 from client 1"]:2: argh
thread status : dead
stack traceback:
[C]: in function 'error'
[string "paste 253 from client 1"]:2: in function 'foobar'
[string "paste 253 from client 1"]:6: in function 'main'
./api/oo.lua:274: in function <./api/oo.lua:271>
(tail call): ?
./api/oo.lua:271: in function <./api/oo.lua:254>
...
> _
You can see that the creature is... well ... brain damaged at the moment. It's code threw an error and is stopped. The creature will continue to do what is was doing before the error. I will likely switch back to the idle
state soon and be an easy prey for other creatures.
You can see the error message in the current message
. This and the traceback will hopefully help you debug any error you encounter.
Lets upload new code:
function Creature:main()
end
If you check the creature information (by typing i) again, you'll see that your creature is still brain damaged. You need to restart your creature by typing r.
This tutorial explained how:
- the world looks like
-
wait_for_next_round()
works - to get information on your creatures
- to debug your creatures