(This tutorial is very much in progress so nonsense is likely.)
Two-dimensional vectors are used a lot in Grobots, mostly to represent positions and velocities. This tutorial is intended to help you understand how to use vector arithmetic to write sides.
Grobots uses vectors for positions and velocities. A vector in Grobots is represented by its two components. For example the grobots code
3 4 pushes 3 and then 4 onto the stack, which can be interpreted as the vector with an x-component of 3 and a y-component of 4.
Vectors can be added with
v+ and subtracted with
For example, if vect-a is a variable holding the vector (2, 5) and vect-b is a variable holding the vector (-1, 3), the code
vect-a vect-b v+
would leave the vector (1, 8) on the stack.
norm returns the length of a vector using the Pythagorean theorem.
vs* and vs/. These can be understood either as multiplying the components by the scalar or by multiplying or dividing the length of the vector by the scalar leaving the direction unchanged.
Summary of Grobots syntaxEdit
(Table here describing v+, v-, vs*, vs/, norm, angle, rect-to-polar, polar-to-rect, unitize, vnegate, dot, cross, and project. Note that all but the first five will be discussed later. Or link to the vector section of the language reference?)
The Grobots built-in
dist computes the distance between two vectors:
p1 p2 dist, where p1 and p2 are vectors, is equivalent to
p1 p2 v- norm or in math notation |p1-p2|. The vector
p1 p2 v- is the vector from p2 to p1; to see this draw a picture. Therefore
p1 p2 v- norm is the distance from p1 to p2.
The Grobots built-in
p1 p2 d in-range, where p1 and p2 are vectors and d is a scalar, determines whether or not p1 and p2 are within a distance of d. It's equivalent to
p1 p2 dist d <.
Many basic gatherers (e.g. the Animal side in the main tutorial) includes the code
food-position seek-location to move the robot towards the food. So what does that do? That code is equivalent to the following:
position velocity 11 vs* v+ food-position radius in-range if engine-max-power engine-power! food-position position v- 0.09 vs* engine-velocity! else 0 engine-power! then
position velocity 11 vs* v+ is where we'll be in 11 frames if we continue moving at our current velocity. The condition of the
if therefore gives us an idea if we're on track to being within our radius of the food soon (and hence close enough to eat it). If we are on track then we turn off the engine to save energy. If not, we turn on the engine with
engine-max-power engine-power!. To set
engine-velocity we first compute the vector displacement that we desire:
food-position position v-. We don't want to do all of this in just one frame, so we multiply by 0.09 to get the velocity. Equivalently we could have written
food-position position v- 11.1111 vs/ engine-velocity!, which shows the interpretation: we want to (ideally) move at whatever velocity would get us there in about 11 frames, so as we approach the destination, we'll slow down.
Chasing shots and unitize operatorEdit
To chase a shot it's helpful to get a vector in the opposite direction as
shot-velocity with length say 0.2. Getting the opposite direction can be accomplished by multiplying by -1, for which a short-hand
vnegate exists. This can be accomplished using operations described so far using
shot-velocity vnegate 0.2 shot-velocity norm / vs*, but that's inconveniently long, especially if shot-velocity is replaced with a more complicated expression. An alternative is to use the
unitize operator, which changes the length of a vector to 1 by doing
2dup norm vs/. We can also multiply the length by -0.2 instead of multiplying by -1 (the vnegate) and then 0.2. In summary The faster way to do the task at the beginning of this paragraph is
shot-velocity unitize -0.2 vs*.
Angles and polar formEdit
So far we've determined the lengths of vectors but we haven't changed the length nor considered direction at all. To quantify direction one can use
angle, which returns the angle between a vector and the x-axis. If the vector points upwards (y-component positive) the angle is positive and if the vector points downwards it is negative. Angles in grobots use radians, where a full revolution/turn is 2pi instead of the customary 360 degrees.
A vector can be represented as x and y coordinates ("rectangular form"), or as a magnitude computed with
norm and a direction computed with
angle ("polar form"). Rectangular form is more convenient for most purposes, but polar form is more convenient for direction-related purposes like firing weapons. The Grobots primitives
polar-to-rect convert between them.
Let's suppose we want to limit the speed we travel to say 0.05 for efficiency. This can be done without polar form: engine-velocity unitize engine-velocity norm 0.05 min vs* engine-velocity!. A better way is via polar coordinates:
engine-velocity rect-to-polar swap 0.05 min swap polar-to-rect engine-velocity!. In this case both versions are 8 instructions, but if engine-velocity is replaced with another vector the polar form version pulls ahead.
One thing that can't be done without polar form is changing the direction of a vector. For example when running away from a shot it's helpful to run at an angle from the shot rather than straight away so that shots in the air don't hit. To flee from a shot at speed 0.2 and direction 0.4 radians from the shot's direction of travel use
0.2 shot-velocity angle 0.4 + polar-to-rect engine-velocity!.
Rotating a vector pi/2 counter-clockwise (90 degrees) is a simple operation using what we've described so far:
rect-to-polar pi/2 + polar-to-rect. Some code uses the shorter but difficult to read
negate swap for the same purpose.
swap negate rotates pi/2 clockwise. So don't be surprised if you see those idioms in sides you read.
Movement in combatEdit
Movement in combat usually has two goals: stay within range of the enemy but only barely, and passively dodge to reduce the chance of being hit. Here's some code based on RK:
Circling a foodEdit
Active 9's gatherers do a form of passive dodging when hurt: circling a food. The relevant code is:
Rotating the coordinate axes and Active dodgingEdit
These topics are described in the Active Dodging Tutorial.