Daniel P. Barron

The drunken explorer.

Saturday, December 17, 2016 

The charge was made that a new mining style should be introduced to foxybot. Just so happens I had already resolved to come up with one for my own purposes. The default ‘line’ method causes characters to drift up hills, i and requires somewhat frequent supervision to keep it in the right spot. Not only this, but the “right spot” is often a small point, or at least a round sort of area; that is, not a thin line. To make the line explore method cover a more reasonable area, I used it in combination with an xmacro script that turned my character periodically, and then sent a pilot command to aim him back at the optimal gathering spot. This was the functionality I intended to add to foxybot, but Mircea’s request for a drunken walk ii method inspired me to finally do it.


Here’s how to add the ‘drunk’ explore method to foxybot.


In botactivity.h, add a new constant iii for this style:

//explore strategies for the explore activity
enum EXPLORE_STRATEGIES
{
	EXPLORE_RADIAL = 1,
	EXPLORE_GRID = 2,
	EXPLORE_LINE = 3,
	EXPLORE_DRUNK = 4
};

In botactivity.cpp:


In ExploreActivity::DoInit(), add this if statement where it checks "words:" iv

			else if (s.CompareNoCase(csString("drunk")))
				strat = EXPLORE_DRUNK;

In MovementStrategy::Move(), add this case to the switch: v

	case EXPLORE_DRUNK:
	{
		if (movesDone == 0) vi
			startPos = worldHandler::GetCurrentPos(); vii
		else if (((double)rand() / (double)RAND_MAX) < ((double)size / ((double)size + (double)pow(size, 0.5)))) viii
worldHandler::Turn(((double)rand() / (double)RAND_MAX) * M_PI * 2); ix
else worldHandler::TurnToPoint(startPos.x, startPos.z); x

worldHandler::StepForward(); xi
}


That's all there is to it!

  1. This is because characters move faster up-hill than they move down-hill, and the difference causes high points to act as bot attractors. ^
  2. It’s very simple. In each iteration of the bot activity, the character turns to a random angle and walks forward one step. ^
  3. Each constant just replaces a number so that instead of saying “mining style #4” we can say “the drunken mining style.” ^
  4. This corresponds to when the user types /bot explore 999 drunk ... ^
  5. This switch is where we can define how drunken mining is to work. ^
  6. First we check if this is the first explore attempt ^
  7. store the character’s starting position. This is used to keep the bot focused on one spot; we don’t want our characters to fall down any cliffs! ^
  8. decide if the bot will move randomly in this itteration or if it will move towards the starting point. It takes the "size" from the user command-- that is, the 24 in /bot explore 999 drunk 24 1 7 ... --and uses it to make a fraction which is compared to a random number. We want the bot to move towards the center square-root-of-size many times per size-plus-square-root-of-size explore attempts, which is the same thing (assuming infinite tries) as moving randomly for 'size' many tries, and then towards the center for square-root-of-size many tries.
    Example of a literal-to-Mircea's-spec version of the drunken logic:
    		if (((double)rand() / (double)RAND_MAX) < (pow(size, 0.5) - floor(pow(size, 0.5))))
    		{
    			if ((movesDone % (int)(size + ceil(pow(size, 0.5)))) < size)
    				worldHandler::Turn(((double)rand() / (double)RAND_MAX) * M_PI * 2);
    			else worldHandler::TurnToPoint(startPos.x, startPos.z);
    		}
    		else
    		{
    			if ((movesDone % (int)(size + floor(pow(size, 0.5)))) < size)
    				worldHandler::Turn(((double)rand() / (double)RAND_MAX) * M_PI * 2);
    			else worldHandler::TurnToPoint(startPos.x, startPos.z);
    		}

    This version is more precise if the supplied 'size' is a perfect square; otherwise the non-integer remainder of the square root is compared against rand() to preserve the substance of non-perfect-square values of 'size' in determining how far to go before re-centering. I leave it up to you to decide which method is the correct. I prefer the simpler, more random way; it more accurately models the movement we desire, which is that the character will move around a particular point in a random manner. The 'size' input lets you define how far the character will stray from the start point. ^

  9. tells the character to turn to a random angle. It simply multiplies 2*pi and a random fraction and feeds it to a function that turns the character. ^
  10. this is where we aim back at the start position. ^
  11. make the character move foward. ^

One Response

  1. [...] I had previously described how to make your Eulora character explore like a drunk. This is a very useful method when the area you are trying to target is closer to a point. The drunk explorer has a strong preference for gathering closer to the starting location; even at large size settings, the distribution will still put most of the attempts near the center. I have created a graph of 10`000 explore attempts using the drunk method in order to demonstrate this property. It looks like a fuzzy paintbrush tool from an image manipulation program. [...]